Binding a collection of data objects in a model to a group of controls in a view (WPF)
another WPF question from me here.
I currently have a situation in which I need to bind an ObservableCollection<T>
of data objects in a model to a group of UserControls in a view. The number of data objects changes often, and I need to create these UserControls on the fly as the collection changes. The current code looks something like this:
private void viewModel_PropertyChanged( object sender, PropertyChangedEventArgs e )
{
if( e.PropertyName.Equals( "SomeCollection" ) )
{
UpdateUI();
}
}
private void UpdateUI()
{
foreach( MyUserControl elem in _uiElement开发者_StackOverflows )
{
elem.MouseLeftButtonDown -= elem_MouseLeftButtonDown;
// more event handler unhooking
_MyCanvas.Children.Remove( elem );
}
_uiElements.Clear();
foreach( DataObj dataObj in ViewModel.DataObjects )
{
MyUserControl newElem = new MyUserControl();
_uiElements.Add( newElem );
fpv.MouseLeftButtonDown += fpv_MouseLeftButtonDown;
// more event handler hookups
newElem.DataObject = dataObj;
_MyCanvas.Children.Add( newElem );
Point dest = dataObj.ScreenPoint;
Canvas.SetLeft( newElem, dest.X );
Canvas.SetTop( newElem, dest.Y );
}
}
So, as you can see, I respond (in code) to a property changed and dynamically create a group of new UserControls. This code is called when a corresponding data object is "Moved" (it contains a Point property), added, created, or the entire collection is set. The problem is that this can take (worst case with 100+ elements) one second or more, which is unacceptable. The controls themselves are simply; only a grid and a shape (Ellipse).
TLDR;
So, I suppose I'm asking if anyone out there has come across a similar situation. Dynamically creating controls in response to a change in a collection and adding/removing them from the UI, similar to how the ItemsSource property works in a ComboBox, ListBox, DataGrid, whatever. The catch is that these controls need to allow for mouse dragging and area laid out in arbitrary locations in a Canvas. Any help is appreciated, thanks in advance.
Why don't you use a virtualized ItemsControl with an ItemTemplateSelector? If the controls are simple it should be possible to use datatemplates for them so no need to create user controls. Since the items control is virtualized only the visible items will be rendered so should be faster.
This is exactly what ItemsControl is for but there are a few wrinkles with layout when you need to use a Canvas and pull the positions from the data. It's not complicated but needs a few different parts to work correctly. Here's a quick sample assuming your ViewModel is the DataContext:
<ItemsControl ItemsSource="{Binding DataObjects}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.ScreenPoint.X}" />
<Setter Property="Canvas.Top" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.ScreenPoint.Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- your UserControl goes here -->
<Ellipse Fill="Red" Width="20" Height="20" ToolTip="{Binding ScreenPoint}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You're also going to want to make sure your DataObjects property is an ObservableCollection and you will then be able to add and remove objects and get automatic updates in the UI of just the items that have changed, helping with your refresh performance problem.
精彩评论