Load a section of a page differently from the rest WPF - wpf

I am creating a WPF application. It contains a window which is filled with a page. Within this page I have a treeview which acts as a menu for my application.
How would I be able to navigate to another page while keeping the treeview unchanged
eg. A users navigates to a page, when that page loads the treeview automatically resets, but I want the treeview to remain as the user left it(opened branches to the last menu item)
At the moment I have the following on each page:
This is why it resets
<TreeView>
<TreeViewItem Header="Items">
<TreeViewItem Header="Item 1"></TreeViewItem>
<TreeViewItem Header="Item 2"></TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="ore Items">
<TreeViewItem Header="Item a"></TreeViewItem>
<TreeViewItem Header="Item b"></TreeViewItem>
</TreeViewItem>
</TreeView>
If the user clicks on Items>Item 1. When the next page loads I'd like the tree view to be opened as the user left it (Items>Item 1) and not reload to default.
Is this possible to do?

Take the TreeView outside of the page so that it persists outside it when the page changes.
For example in your window you may have a grid where the TreeView is in row 0 and the Frame that hosts your pages is in row 1.
The other thing you can do is save the state of the TreeView to a value that is within the datacontext of all pages that use the TreeView. This will allow the state of the TreeView to be preserved between navigations.

Related

WPF Access datagrid in tabs

I have a templated tabcontrol with templated datagrids in each tab, like this:
<TabControl>
<TabControl.ItemTemplate >
<DataTemplate>
....
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DataGrid>
<DataGrid.ColumnHeaderStyle>
...
</DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
...
</DataGrid.CellStyle>
</DataGrid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I omitted data bindings in the code so it reads better.
I want to specify a tab, and a cell in the datagrid (row and column) and after clicking a button make the program open the specified tag and scroll down through the table to highlight the specified cell automatically, similar to what is done in visual studio when you click on a compile error and it takes you to the line in the file where the error is.
I am changing the selected tab with Tab.SelectedIndex, and that works, but I can't access the datagrid inside of the tab because it is only generated when the tab is clicked manually. I tried using Load but it doesn't work. How can I generate and access the datagrid inside each tab?
Is it because it's being loaded asynchronously? Try using performing your grid selection after loading using the Loaded event.
TabItem selectedTab = MyTabControl.SelectedItem as TabItem;
selectedTab.Loaded+= delegate { //select grid row };
//or if this works, I'm sure it would have been initialized in the process of being selected?
TabItem selectedTab = MyTabControl.SelectedItem as TabItem;
selectedTab.MyGrid...
Or maybe selectedTab.Initialized.
The tab items are also children of the tabControl, so you should be able to access them using Children[index] as TabItem

How to avoid creation of a view if it is already exist

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?

ContentControl inside a WPF TreeView

I am trying to add a content control inside a treeview, but when I add treeview items inside content control they are aligned further away from other treeview items.
<TreeView>
<TreeViewItem Header="XXX-1"></TreeViewItem>
<TreeViewItem Header="XXX-2"></TreeViewItem>
<ContentControl>
<TreeViewItem Header="YYY-1"></TreeViewItem>
</ContentControl>
<TreeViewItem Header="XXX-3"></TreeViewItem>
</TreeView>
The above code results in a treeview like below.
XXX-1
XXX-2
YYY-1
XXX-3
I think ContentControl adds another TreeViewItem by itself. How can I align the TreeViewItems together?
I would start with Rachel's answer but remove the ContentControl. You can accomplish this with two DataTemplates and an ItemTemplateSelector assigned to the TreeViewItem's ItemTemplateSelector property.
Define your complex and simple types into two DataTemplates. Then write a class which inherits from DataTemplateSelector that determines if the complex or simple type should be used in the TreeViewItem. You then set the TreeViewItem's ItemTemplateSelector to the DataTemplateSelector object you just created. Here's is an example: http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemtemplateselector.aspx.
Here is another link that shows you how to select a DataTemplate based on a condition: http://msdn.microsoft.com/en-us/library/ms742521.aspx. Search for the header Choosing a DataTemplate Based on Properties of the Data Object.
It does nest the ContentControl in a TreeViewItem (see Snoop screenshot below)
Snoop also tells me that the extra margin is for the +/- Expander, so you'll probably have to overwrite the template to get rid of that margin if you want to keep your TreeViewItems nested.
Of course, if you're simply trying to place a ContentControl inside your TreeViewItem, the tags should be the other way around.
<TreeViewItem Header="YYY-1">
<ContentControl />
</TreeViewItem>

WPF TreeView Nodes becomes unselectable after adding children nodes

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.

How do you get data from controls on pages on a Tab Control

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.

Resources