I have a listbox which looks like this:
<ListBox ItemsSource="{Binding Clients}">
<ListBox.ItemTemplate>
<DataTemplate>
<controls:ClientItem />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
There are several tabs in my window and the Clients collection is constructed depending on a selected tab. The problem is that the ClientItem constructor is called every time I switch the tabs, and the gui works slowly. There are normally 10-20 clients in the listbox and I can see in the vs profiler that a ClientItem constructor consumes the most part of time.
Is it possible to tell wpf not to create ClientItem view if it already exists?
Related
PREFACE
I created two different data DataTemplates to allow faster load time of my datagrid cells. One is basically a textblock, the other a more expensive UserControl. On load, the data template selector decides whether it needs to load a cheap vs. more expensive control, which increases first time load/performance from ~12 seconds down to ~2, because most of the cells don't need to be complex and the time that it takes to load a simple template is virtually nothing compared to the other.
Anyway, it works great on load. But, when I change the data underneath, which raises the property changed and should force the DataTemplateSelector to call SelectTemplate() again to re-evaluate which data template to be used - well, it doesn't.
What is interesting is that, after above failure, when I click on a cell, it does call the SelectTemplate() and flips the DataTemplate if needed.
CODE
<DataGrid>
.....
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Intervals[0].Self}"
ContentTemplateSelector="{StaticResource TxtVsExpensiveCell_TemplateSelector}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
Note, the more complex control that is selected by the DataTemplate Selector works perfectly as I had it tested without the DataTemplateSelector.
Also, a note on Binding Intervals[0].Self - self is a property that returns "this", the ref of the Object that I am binding to, the only reason for that, is to (when the Self is raised) to force the DataTemplate to be re-evaluated. Apparently that doesn't quite work.
QUESTION
How do I force the data template to re-evaluate?
Supporting code*
this is my resource dict (nothing fancy)
<DataTemplate x:Key="SimpleTemplate">
<TextBlock DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Key="ComplexTemplate">
<Views:ComplexCell DataContext="{Binding}"/>
</DataTemplate>
<Views:MyTemplateSelector
x:Key="TxtVsExpensiveCell_TemplateSelector"
SimpleTemplate="{StaticResource SimpleTemplate}"
ComplexTemplate="{StaticResource ComplexTemplate}"/>
and obviously I have MyTemplateSelector class that overrides SelectTemplate and works just fine...
I'm working on a WPF application that contains a wrapper UI that is actually the MainWindow.xaml and the content in it (ContentPresenter) gets changed each time the user chooses to move to a different section of the application.
When the user returns to the main content, I want the application not to initialize it each time, but to preserve it in the memory somehow and restore it when the user clicks on the "Home" button.
In its current state, the Home view gets initialized over and over again when moving back to the "Home" section, which causes the application to be kinda slow. My question is this: Is there a way to preserve that user control in the memory somehow so that I would be able to restore it fast?
Thanks!
Hiding is an option ofcourse as #Matt suggested. An other way is to explorer the wonderfull world of frameworks (Prism, Caliburn, Caliburn.Micro, ... )
Those deliver a great assistance in managing WPF applications.
In essence you need to keep a reference to the ViewModel, a collection in the mainviewmodel or mainview that keeps track of the loaded viewmodels.
Eventually I decided to move the ContentControl into an ItemsControl and then just hide all content controls except for the active one.
Here's a snippet of my code:
<ItemsControl ItemsSource="{Binding ViewModels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Visibility="{Binding Visibility}" Content="{Binding ViewModel}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In my ViewModel I have the actual content and another Visibility property that is set to false when the content is not the current one and true if it is.
Thanks to all helpers!
Any help on this really appreciated. In summary I'm trying to databind to properties of a custom class instantiated in xaml that then forms the content of a templated listboxitem (phew!).
I have a simple c# class called MenuItem. It has two properties:
- Heading
- Icon
Concentrating on just one of those menu items (i.e. to provide a simple example of where I am stuck) If I do this (with the values hard coded) it works fine:
<ListBox>
<ListBoxItem ContentTemplate="{StaticResource MenuItemTemplate}">
<myclasses:MenuItem Heading="News" IconImage="News.png"/>
</ListBoxItem>
</Listbox>
Where MenuItemTemplate is an appropriate DataTemplate in the resources section binding each property) containing lines such as:
<TextBlock x:Name="tbHeading" Text="{Binding Heading}">
Wheareas when I try to use binding to set the Heading property it falls over (AG_E_PARSER_BAD_PROPERTY_VALUE error)- e.g.:
<ListBox>
<ListBoxItem ContentTemplate="{StaticResource MenuItemTemplate}">
<myclasses:MenuItem Heading="{Binding NewsHeading, Mode=OneWay}" Icon="News.png"/>
</ListBoxItem>
<Listbox>
I've wondered if it is because I'm doing some kind of double binding (i.e. the template is binding to a value on the MenuItem class that needs to be bound) and that's not possible? I've tried having the properties declared as dependency properties but no difference (although I only learned about those today so I may be missing something).
I know I could set the menuitem objects up in the view model, and bind from there, but I would like to understand why the above doesn't work (as for my purposes there are advantages in constructing the menu items in the xaml).
Thank you!!!!
Ian
thanks for sticking with this. I agree the listbox might not be needed - but even if I reduce it to just one item in a contentcontrol:
<ContentControl ContentTemplate="{StaticResource MenuItemTemplate}">
<myclasses:MenuItem Heading="{Binding NewsHeading, Mode=OneWay}" IconImage="News.png"/>
</ContentControl>
I still have the same problem - which is that I can get databinding to work within the content of a contentcontrol (prior to it being presented by the datatemplate referred to in ContentTemplate) using purely xaml.
I.e. the above bit of xaml doesn't work - it throws an error on the bit that binds the NewsHeading:
Heading="{Binding NewsHeading, Mode=OneWay}
So I am trying to understand whether what I'm doing is impossible, or whether it is but I'm doing it wrong.
Thanks.
Assuming that you have multiple MenuItem classes (because you're putting them in a listbox and ti wouldn't make sense to do that if you just had one). You need to bind the collection to the ItemsSource property of the ListBox.
Somehting like this:
<ListBox ItemsSource="{Binding MyMenuItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Heading}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note that the above assumes you've set the DataContext on the page to an object with a property called MyMenuItems which is a collection of your MenuItem objects.
To see a full example of this, look at the default code created when you create a new "Windows Phone Databound Application".
Edit:
Based on your comments, it seems that a ListBox is not the most appropriate solution to your needs. A ListBox is designed/intended to take a collection of items and display them in a list.
If you have a number of different objects which you know about at design time and simply wish to have them one on top of another (giving the appearance of a list) you could simply put them inside a ScrollViewer and/or a StackPanel (or other appropriate container). Plus, you would still be able to databind if you did it this way.
I have a huge issue with the WPF TreeView. I have setup a tree that is similar to this.
<TreeView DataContext="{Binding Projects}">
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
<TreeView.Resources>
<HierarchicalDataTemplate x:Key="LoadTemplate">
<Grid>
<TextBlock Text="{Binding Name}" />
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="StepTemplate"
ItemsSource="{Binding Loads}"
ItemTemplate="{StaticResource LoadTemplate}">
<Grid>
<TextBlock Text="{Binding Name}" />
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="ProjectTemplate"
ItemsSource="{Binding Steps}"
ItemTemplate="{StaticResource StepTemplate}">
<Grid>
<TextBlock Text="{Binding Name}" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeViewItem Header="Project Workspace"
ItemsSource="{Binding}"
IsExpanded="True"
ItemTemplate="{StaticResource ProjectTemplate}" />
</TreeView>
The TreeView is bound to a DependencyProperty called Projects which is an ObservableCollection in the code-behind file for the xaml. The basic structure of the classes that the tree is bound to follows the structure of the tree. So Project contains a custom collection that implements both the INotifyPropertyChanged and ICollectionChanged interfaces. That collection is accessible through a property called Steps and contains Step objects. The Step class also has an instance of the same custom collection accessable from a property called Loads and contains Load objects.
Now when I work with any object within the Projects, the tree behaves correctly in that nodes appear and dissappear and change correctly as the set of nested collections change. For the most part, everything works as it should.
Althought I didn't show it in the Xaml above, each of the nodes above have their own specific context menu added. One of the options in that context menu is to add an object. So for instance, if you right click on a Step node, you have the option to add a Load node beneath it. Adding the Load class into the Loads property in the Step object does cause a node to show up in the tree just fine.
So for instance the code looks something similar to this:
Projects[0].Steps[0].Loads.add(new Load());
Ok, here is the problem that I've been trying to figure out for the longest time now. After that new node shows up in the tree, it's parent node is no longer selectable. So in the given example, you cannot select the Step node that owns the newly added Load node. You can still do things like DoubleClick and RightClick and get events to fire, but simply attepting to single click with the left mouse button will not cause the Step node to be selected or have focus. I've tried everything I can think of and I cannot for the life of me figure out why not.
Now I can click around on other nodes in the tree and they are selecatable just fine. And after doing so, the node that previously would not allow being selected then regains it's former functionality and is once again able to be selected.
So what happens is that you add a new node, it shows up and it's parent can't be selected until you select some other part of the TreeView and then everything is fine again.
With no help and nothing I tried having worked, I switched to using Telerik controls. After replacing the default TreeView with the RadTreeView from Telerik, all my issues disappeared. I'd suggest if possible that people avoid Microsoft's built in WPF TreeView control as it is plagued with countless issues and you'll spend a great amounts of time trying to deal with them, rather than developing your application.
I've got a WPF tab control that contain several duplicate controls as Tab Page content
<TabControl ItemsSource="{Binding}" Name="tabControl">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<local:InnerDataEntryControl DataContext="{Binding Data}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
On the InnerDataEntry control there is a list box. I've got a command on the outer form that requires the selected items from the list box on the control. I can't figure out how to access the list box on the tab control itself. When I try to query the selected items, I get the bound items and not the list box itself.
I don't want to pollute the business layer with an 'IsSelected' property on my list items, and I suppose I could create a view model if necessary, but it just seems wrong that I can't get information about the actual content control of a tab page.
I hope that I'm just missing something obvious.
This was asked earlier in my WPF experience. To close the loop on the question, I'm going to post a link to the MSDN Magazine entry on the subject of MVVM.
Ultimately, the solution involves the creation of a view model that has the necessary properties bound to the parts of the tab control such that the view model doesn't need access in the way that I'm describing. Instead, the view model acts directly on the data that that is bound without having to reference the view directly.