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.
Related
Sorry for my bad headline but I couldn't figure out a better one. Heck, I don't even know how to properly ask my question. But here it comes.
I have a custom control in WPF, let's call it Cell. This control does have a few dependency properties, one Text to show in the Cell and a few "decorative" properties for background color, foreground color and so on. If I use this Cell control stand-alone, everything works fine so far.
Then I have another custom control inheriting from ItemsControl, let's call it Field. This control should show a text and some Cells. it has some "decorative" properties for the text part as well. The information about showing Cells is given to the control by a DataTemplate, something like
<DataTemplate x:Key="CellTemplate"
DataType="...">
<ns:Cell Text="{Binding Text}"
Background="{Binding ...}" />
</DataTemplate>
...
<ns:Field ItemsSource="{Binding Cells}"
ItemTemplate="{StaticResource CellTemplate}" />
If I use this Field control stand-alone, everything works fine so far.
Now I want to show several Field controls at once. So I put an ItemsControl on my window and gave an ItemTemplate again, something like:
<DataTemplate x:Key="CellTemplate"
DataType="...">
<ns:Cell Text="{Binding Text}"
Background="{Binding ...}" />
</DataTemplate>
<DataTemplate x:Key="FieldTemplate"
DataType="...">
<ns:Field ItemsSource="{Binding Cells}"
ItemTemplate="{StaticResource CellTemplate}"
FieldText="{Binding Text}"
TextBackground="{Binding ...}" />
</DataTemplate>
<ItemsControl ItemsSource="{Binding Fields}"
ItemTemplate="{StaticResource FieldTemplate}" />
As long as I preview my WPF window everything works fine so far. Changing the values of some "decorative" properties at Cell level or at Field level is immediately shown in the preview.
But if I run my program it seems that all "decorative" properties at Cell level are ignored. I can see all my Fields with their respective texts and I can see every Cell in every Field with their respective texts. But all Cells are plain white. All set colors are not shown.
Snoop tells me, that every color is set to Transparent by the ParentTemplate.
Visual Studio doesn't show me any exceptions or any binding errors. So I'm kind of stuck at where or how I can find the error and fix it.
So I wanted to ask you, if you may have a hint for me.
Does this contruction with a DataTemplate containing a DataTemplate and both DataTemplates bind to it's DataContext work?
Or does it have something to do with maybe re-using Brush objects that shouldn't be re-used?
But why is it working in the preview?
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?
I want to create a WPF control similar to the example below. Check out the link and look at the navigation control on the left.
Can this be done in a treeview? If so, any idea how I would start?
If not a treeview, then how could I achieve the same thing?
Navigation example
The parent node has a different style to the child node and in some cases a parent will have child nodes and some won't. I'm not sure how to style a control that will give me the same look. Any advice would be greatly appreciated.
Thanks,
Since you can style different nodes of a treeview as you like, the answer is yes, you can. You have to bind a treeview to an IEnumerable<A>, where every object of type A will have an IEnumerable<B> (which can be empty).
You can then apply one style to every element of type A, and another style to every element of type B. In XAML, in TreeView.Resources, put two HierarchicalDataTemplates with DataType attributes. For example, if you have an IEnumerable of Categories, and each Category has a property Items which is an IEnumerable of Items, you may write:
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type core:Category}">
<!--Content here-->
<HierarchicalDataTemplate.ItemsSource>
<Binding Path="ContextAssociations"/>
</HierarchicalDataTemplate.ItemsSource>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type core:Item}">
<!--Content here-->
</HierarchicalDataTemplate>
</TreeView.Resources>
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.
I have the following code and basically what i am not able to figure out is how to clone the whole grid and make a blank copy of them side by side.... for a clear understanding this is something to do with hospital application and the grid is related to a pregnancy so when said 'ADD CHILD' button a whole new grid should be created during run time, thanks for the help below is a link that might help people cause i tried it but not sure how to display it
How can you clone a WPF object?
You should put the object you are want to "clone" in a DataTemplate and reference this template from an ItemsControl, then when you need another grid add another item to the items control (or even better to the list the control is bound to) and the ItemsControl will create a new grid and bind it the new object.
For an example take a look at this post on my blog.
Here is an example for this application (I left only the relevant parts and I didn't test it, so there are probably some typos there):
<Window ... >
<Window.Resources>
<DataTemplate x:Key="ChildTemplate">
<Grid>
...
<TextBlock Text="Delivery Date:" Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding DeliveryDate}" Grid.Column="1" Grid.Row="0"/>
<TextBlock Text="Delivery Time:" Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding DeliveryTime}" Grid.Column="1" Grid.Row="1"/>
...
</Grid>
</DataTemplate>
</Window.Resources>
...
<Button Content="AddChild" Click="AddChildClick"/>
...
<ScrollViewer>
<ItemsControl ItemsSource="{Binding AllChildren}" ItemsTemplate="{StaticResource ChildTemplate}">
<ItemsControl.PanelTemplate>
<ItemsPanelTemplate><StackPanel Orientation="Horizontal"/></ItemPanelTemplate>
<ItemsControl.PanelTemplate>
</ScrollViewer>
...
</Window>
And in cs:
Set an object with all the form data as the Window's DataContext. I'll call this class PostDelveryData.
Create another class with the repeating data. I'll call it ChildDeliveryData.
Add a property of type ObservableCollection<ChildDeliveryData> called AllChildren to PostDeliveryData; it's important it'll be ObservableCollection and not any other type of collection.
Now, for the magic:
private void AddChildClick(object sender, RoutedEvetnArgs e)
{
((PostDeliveryData)DataContext).AllChildren.Add(new ChildDeliveryData());
}
And when you add the new item to the list another copy of the entire data template will be added.
I'm not sure that you're using the correct approach here. I would approach the problem by creating a "ChildGridControl" with a Child property, and let the Child property handle the databinding. Adding a new child to the GUI would involve creating a new instance of the ChildGridControl.
If I am understanding correctly, you should create a UserControl, which wraps your Grid and subsequent controls inside. And use this User control anywhere you wanted to replicate that UI.