How to keep UI running while Observable collection is loading?
I have the synfusion tile view as below.
Maximized Item template for these Items is TreeView. the treeview items Source is bind to the Observable Collection. When I Maximize one of these Items, it will load the data from ViewModel as below. It's in the MaximizedItemChanged Event.
private void tileViewControl_Exchanges_MaximizedItemChanged(object sender, TileViewEventArgs args)
{
if (args.Source != null)
{
TileViewControl tileViewControl = (TileViewControl)sender;
TileViewItem tvi = (TileViewItem)args.Source;
PanelViewModel panelViewModel = (PanelViewModel)tileViewControl.ItemContainerGenerator.ItemFromContainer(tvi);
String currentSelectedPanelID = GetSelectedPanelID(tileViewControl);
// below function will load all the treeview items.
SetSelectedExchangeID(tileViewControl, exchangePanelViewModel.ExchangeID);
}
}
But treeview has over thousands of items. So after clicking on Maximize, It will take a while and the program hang. Is there a way to maximize the Item smoothly first and load the Treeview Item at the background? What I'd like to do is I will show the loading animation while the treeview is loading but now, when it maximized (after hanging for 8 or 9 secs) , the treeview is already loaded.
Edit: I added the code fore SetSelectedExchangeID.
public static readonly DependencyProperty SelectedExchangeIDProperty =
DependencyProperty.RegisterAttached("SelectedExchangeID",
typeof(String),
typeof(UC_Contract_List),
new UIPropertyMetadata(new PropertyChangedCallback(SelectedExchangeIDPropertyChanged)));
static void SelectedExchangeIDPropertyChanged(
DependencyObject depObj,
DependencyPropertyChangedEventArgs eventArgs)
{
TileViewControl tileViewControl = (TileViewControl)depObj;
ItemContainerGenerator itemContainerGenerator = tileViewControl.ItemContainerGenerator;
String newPanelID = (String)eventArgs.NewValue;
if (newPanelID != null)
{
if (tileViewControl.Visibility == Visibility.Visible)
{
foreach (PanelViewModel exchangePanel in tileViewControl.Items)
{
if (exchangePanel.ExchangeID.Equals(newExchangeID))
{
TileViewItem tvi = (TileViewItem)itemContainerGenerator.ContainerFromItem(exchangePanel);
try
{
if (tileViewControl.tileViewItems != null)
{
if (tvi.TileViewItemState != TileViewItemState.Maximized)
{
tvi.TileViewItemState = TileViewItemState.Maximized;
}
}
}
catch (Exception e) { }
break;
}
}
}
}
else
{
foreach (PanelViewModel exchangePanel in tileViewControl.Items)
{
TileViewItem tvi = (TileViewItem)itemContainerGenerator.ContainerFromItem(exchangePanel);
tvi.TileViewItemState = TileViewItemState.Normal;
}
}
}
public static void SetSelectedExchangeID(DependencyObject depObj, String exchangeID)
{
depObj.SetValue(SelectedExchangeIDProperty, exchangeID);
}
public static String GetSelectedExchangeID(DependencyObject depObj)
{
return (String)depObj.GetValue(SelectedExchangeIDProperty);
}
And in ViewModel
:
St开发者_JAVA技巧ring _selectedExchangeID;
public String SelectedExchangeID
{
get { return this._selectedExchangeID; }
set
{
if (value == null)
{
this.ClearPanels();
this._selectedExchangeID = value;
}
else
{
this._selectedExchangeID = value;
PanelViewModel curPanelViewModel = this.GetPanelViewModel(this._selectedExchangeID);
if (curPanelViewModel != null)
{
curPanelViewModel.Activate(); // this will add to the observable collection for Treeview ItemsSource
}
}
this.OnPropertyChanged("SelectedExchangeID");
}
}
You can do that by doing the processing/heavy loading task asynchronously on a background thread and syncing with foreground thread using UI Dispatcher object only when everything is available and processed.
For details on BackgroundWorker refer to MSDN.
Please note BackgroundWorker is not the only way to do async task. you may opt for Tasks (introduced in .net 4.0) or BeginInvoke/EndInvoke.
And When you are done with Heavy task you may sync with foreground thread in the following way. First initialize dispatcher on UI thread (lets say Views Constructor):
Dispatcher _UIDispatcher;
public MyView
{
...
_UIDispatcher = Dispatcher.CurrentDispatcher;
}
Then sync in after loading is complete:
public void SyncPostLoading(IEnumerable<Something> myData)
{
_UIDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, System.Threading.ThreadStart)delegate()
{
foreach(Something something in myData)
myObervableCollection.Add(something);
});
}
You have a couple of different options for how to do you work on a background thread. You can use the backgroundworker (slightly outdated) or the .NET 4.0 Tasks (part of the Task Parallel Library). You need to decide if you want to load all of the data into a new collection and invoke an update onto the GUI thread all at once or if you want to load the items in batches and invoke those batches onto the GUI in several rounds
精彩评论