Nested Datagrid in ListBox - wpf

I have a datagrid nested inside the ItemTemplate of a ListBox. I'm trying to display a tree like data structure using this. My classes are as follows.
The object in my data context contains a List<Section> named Sections, my ListBox is bound to this. Each Section contains a List<Item> named Items, the DataGrid in eac ItemTemplate is bound to this.
When I run the app, I get a null reference exception from the XAML at the line with the binding. Is there a better/alternative way of doing this, or am I missing a trick with the binding?
<Window.Resources>
<CollectionViewSource x:Key="SectionSource" /><!-- this is initialized and filled with an ObservableCollection<Section> Sections when the window loads-->
</Window.Resources>
<ListBox x:Name="lstIngredients" ItemsSource="{Binding Source={StaticResource SectionSource}}">
<ListBox.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<CollectionViewSource x:Key="itemsSource" Source="{Binding Items}"/>
</DataTemplate.Resources>
<DataGrid x:Name="dgItems" IsReadOnly="false" AutoGenerateColumns="False" SelectionMode="Single" SelectionUnit="FullRow" IsSynchronizedWithCurrentItem="True"
DataContext="{Binding}"
ItemsSource="{Binding Source={StaticResource Items}}"
EnableRowVirtualization="false"
VirtualizingStackPanel.VirtualizationMode="Standard"
<DataGrid.Columns>
<DataGridTemplateColumn Width="2*" Header="{lex:LocText ChickenPing.Shared:Strings:Measurement}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="quantity" Text="{Binding Measurement}" TextTrimming="CharacterEllipsis" TextAlignment="Left"/>
<!-- Null reference on this line caused by the binding. If I set this to any DependencyProperty on an Item object, I get a null reference-->
</DataTemplate>

This need to be path
ItemsSource="{Binding Source={StaticResource Items}}"
ItemsSource="{Binding Path=PropertyThatIsCollection}"
And delete the DataContext line

I eventually tracked this down to an event which was set in one of the TemplateColumns. Switching the event from
<TextBlock x:Name="quantity" Text="{Binding Measurement}" GotFocus="txt_GotFocus" />
to
<Style x:Key="FocusableTextbox" TargetType="{x:Type TextBox}">
<EventSetter Event="GotFocus" Handler="txt_GotFocus" />
</Style>
...
<TextBlock x:Name="quantity" Text="{Binding Measurement}" Style={StaticResource FocusableTextbox} />

Related

How to set ItemsSource of a ComboBox, if it was present in an ItemsCotrol

ComboBox items were not displaying, if we try to keep that combobox in an ItemsCotrol. Please click here for understanding my requirement
My requirement is to keep a combobox in an ItemsControl, so that the ItemsControl qill be having 5 Comboboxes in it and each combox will be having a collection of items which we can select. So for that i tried with the below code and able to get the comboboxes in the ItemsControl, but the comboboxes collection is getting filled, any suggestions or workaround please..
<xamDataPresenter:Field Label="Reqs" BindingType="Unbound" Row="0" Column="4">
<xamDataPresenter:Field.CellValuePresenterStyle>
<Style TargetType="{x:Type xamDataPresenter:CellValuePresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type xamDataPresenter:CellValuePresenter}">
<ItemsControl Name="I" ItemsSource="{Binding Path=DataItem.CollectionCount}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataItem.Collection}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</xamDataPresenter:Field.CellValuePresenterStyle>
</xamDataPresenter:Field>
Ok, I've written up the most basic of ItemsControl to try and explain how these things work, which you can hopefully adapt for whatever your using for your dataitems.
So in your window's resources i've created a datatemplate. This respresents a repeating step and will be based on a DataItem. In this case my DataItem has 2 properties: DataItemProperty(string) and SelectedItem. The SelectedItem will have the same DataType of whatever it is your planning on showing in the combobox.
<DataTemplate x:Key="StepTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<TextBlock Text="{Binding Path=DataItemProperty}" Grid.Column="0"/>
<ComboBox Grid.Column="1" ItemsSource="{Binding Path=DataContext.ItemsToSelectFrom, Mode=OneWay, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
The combobox in this example will be getting a list of available options from the code behind/viewmodel and not the DataItem, but when you select something it updates the SelectedItem property on the DataItem.
Then to show your items:
<ItemsControl
Focusable="False"
ItemTemplate="{StaticResource StepTemplate}"
ItemsSource="{Binding Path=Steps, Mode=OneWay}" />
So Steps is a property in my codebehind/viewmodel that will determine how many 'rows' get displayed.
The itemsControl allows you to add repeating sets of data easily, without having to write the same xaml multiple times.
Hope that helps?

WPF DataBinding issue: UserControls in DataGrid Cells not binding

I am trying to build up a DataGrid control for managing a property grid. I have an abstract class for my parameter base class that parameters of various types implement. This base class contains variables that are shared across each parameter (like Group, Name, IsLocked, etc.), and then each parameter manages the information that relevant to the particular type. For example, a number parameter may use a slider for the user control, which also then uses min/max values, etc.
I have set up a class which is an Observable Collection of my base parameter, and established it as a static resource for a CollectionViewSource:
<UserControl.Resources>
<local:Parameters x:Key="Parameters"/>
<CollectionViewSource x:Key="cvsParameters" Source="{StaticResource Parameters}" />
</UserControl.Resources>
Then in my DataGrid, I am using style triggers to switch between user controls for each of my parameters:
<DataGrid x:Name="DataGrid_Globals"
ItemsSource="{Binding Source={StaticResource cvsParameters}}"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Key" Binding="{Binding Key}" IsReadOnly="True"/>
<DataGridTemplateColumn Width="*" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding UIType}" Value="Text">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<controls:ucText DataContext="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding UIType}" Value="Number">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<controls:ucNumber DataContext="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
My user controls are set up with their data context declared in XAML:
<UserControl.DataContext>
<local:TestNumber_cls/>
</UserControl.DataContext>
<Grid HorizontalAlignment="Stretch">
<Slider
Value="{Binding NumberValue}"
Minimum="{Binding Min}"
Maximum="{Binding Max}"
/>
</Grid>
I can't figure out how to ensure that my usercontrols bind correctly. My other column works fine, with each Key value appearing, being correctly bound and updating (for this example it's just the "Key" column, but all of the fields from the base class behave just fine.) The correct UserControls are also selected based on the UIType DataTrigger, but the UserControls just instantiate from an empty constructor and don't bind.
I have also confirmed that standalone instances of my UserControls work fine and bind correctly. For, example, it works when I set the datacontext from code behind (which I don't want to do):
this.OneOffNumberUserControl.DataContext = ((Parameters)this.ParameterGrid.Resources["Parameters"])[2];
I have also tried to remove the DataContext="{Binding}" from my user controls, but this doesn't change anything.
In conjunction with Mechanic's suggestion above, which greatly simplified the XAML for my property grid, through further research I also learned that I needed to implement a DependencyProperty for each subclass-dependent usercontrol. Where I set the DataTemplate for each DataType in my property grid UserControl, I set the binding to my DependencyProperty.
<UserControl.Resources>
<local:Parameters x:Key="Parameters"/>
<CollectionViewSource x:Key="cvsParameters" Source="{StaticResource Parameters}" />
<DataTemplate DataType="{x:Type local:TestText_cls}">
<controls:ucText TextObject="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:TestNumber_cls}">
<controls:ucNumber NumberObject="{Binding}"/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<DataGrid x:Name="DataGrid_Globals"
ItemsSource="{Binding Source={StaticResource cvsParameters}}"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Key" Binding="{Binding Key}" IsReadOnly="True"/>
<DataGridTemplateColumn Width="*" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Then in the code behind my UserControls, I implement the DependencyProperty:
public static readonly DependencyProperty TextObjectProperty =
DependencyProperty.Register("TextObject",
typeof(TestText_cls),
typeof(ucText),
new PropertyMetadata(new TestText_cls()));
[Bindable(true)]
public TestText_cls TextObject
{
get { return (TestText_cls)this.GetValue(TextObjectProperty); }
set { this.SetValue(TextObjectProperty, value); }
}
public ucText()
{
InitializeComponent();
}
Finally, within each UserControl, I ensure that I have given it a name, and then set my binding to the correct variable in my DependencyProperty:
<UserControl x:Class="Testing.ucText"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Testing.Classes"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Name="TextUI">
<Grid HorizontalAlignment="Stretch">
<TextBox Text="{Binding ElementName=TextUI, Path=TextObject.Value}"/>
</Grid>
</UserControl>
I hope this helps anyone else!
If you want to put different controls for different subclasses, specify different datatemplates, and then
<!-- subtype-specific column -->
<DataGridTemplateColumn Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

CollectionViewSource: TreeView not updating, ViewModel looking good

I'm trying to populate a TreeView in a TestExplorerControl:
I never had to use a CollectionViewSource until now, so I used this tutorial to get a grasp of how to group my ObservableCollection<TestMethod> in XAML, and use that grouping for my tree view - I implemented the data templates in the <UserControl.Resources>, because I want the flexibility to eventually allow the user change the way tests are regrouped:
<CollectionViewSource x:Key="OutcomeGroupViewSource" Source="{Binding Model.Tests}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Result.Outcome" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<DataTemplate x:Key="TestMethodTemplate" DataType="{x:Type local:TestMethod}">
<StackPanel Orientation="Horizontal">
<Image .../>
<TextBlock .../>
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="OutcomeTemplate" DataType="{x:Type CollectionViewGroup}"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource TestMethodTemplate}">
<StackPanel Orientation="Horizontal">
<Image ../>
<TextBlock ../>
<TextBlock ../>
</StackPanel>
</HierarchicalDataTemplate>
Then I have this markup for the actual <TreeView>:
<TreeView Grid.Row="2"
ItemsSource="{Binding Source={StaticResource OutcomeGroupViewSource}, Path=View.Groups}"
ItemTemplate="{StaticResource OutcomeTemplate}" />
What's wrong with this markup, for the TreeView to fail updating? The ViewModel clearly has all the data I need to display (the breakpoint that was hit is on the current line, in yellow):
Found it. It's the Path of the ItemsSource binding in the tree view:
ItemsSource="{Binding Source={StaticResource OutcomeGroupViewSource}, Path=View.Groups}"
Path=View.Groups satisfies IntelliSense, but is wrong.
It needs to be Path=Groups, even if the designer supposedly can't resolve the property:

WPF : How to create separate Resources for each item in a bound ItemsControl

I want to achieve the following:
My ViewModel exposes a property named 'Categories' which is a collection of CategoryViewModel objects
Each CategoryViewModel object exposes a property called 'Items' which is a collection of strings*.
On my View, I want a TabControl with a TabItem for each object in the 'Categories' collection.
The Content of each TabItem should be a xceed DataGrid control displaying the contents of the selected tab's Items collection.
<TabControl ItemsSource="{Binding Categories}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<xcdg:DataGridControl
ItemsSource="{Binding Items}"
AutoCreateColumns="True">
</xcdg:DataGridControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This works ok when I bind directly to the ItemsSource property of the DataGridControl. However, in order to utilize all of the functionality of the DataGridControl, I need to bind the ItemsSource property of the DataGridControl to a DataGridCollectionViewSource object that is bound to my Items collection. I do this when the grid ISN'T nested in another control by creating a DataGridCollectionViewSource object in the Resources section of the UserControl and binding to that.
<UserControl>
<UserControl.Resources>
<xcdg:DataGridCollectionViewSource x:Key="GridData"
Source="{Binding Items}" />
</UserControl.Resources>
<Grid>
<xcdg:DataGridControl
ItemsSource="{Binding Source={StaticResource GridData}}"
AutoCreateColumns="True">
</xcdg:DataGridControl>
</Grid>
</UserControl>
How do I need to structure the XAML so that when the TabControl is being bound, a DataGridCollectionViewSource object is created for each TabItem so that the DataGridControl that is generated within the content of the TabItem can be bound to it?
Clear as mud, right? :)
Thanks!
Notes:
*In the real solution the collection contains objects of a class that is more complex than a simple string, but a string was used to make the example more simple.
OK, this is a bit of a long-shot, but could you use the DataGrid.Tag ...
<TabControl.ContentTemplate>
<DataTemplate>
<xcdg:DataGridControl
ItemsSource="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"
AutoCreateColumns="True">
<xcdg:DataGridControl.Tag>
<xcdg:DataGridCollectionViewSource x:Key="GridData"
Source="{Binding Items}" />
</xcdg:DataGridControl.Tag>
</xcdg:DataGridControl>
</DataTemplate>
</TabControl.ContentTemplate>
Or ... resources can be defined on any FrameworkElement, so you could try:
<TabControl.ContentTemplate>
<DataTemplate>
<xcdg:DataGridControl
ItemsSource="{Binding Source={StaticResource GridData}}"
AutoCreateColumns="True">
<xcdg:DataGridControl.Resources>
<xcdg:DataGridCollectionViewSource x:Key="GridData"
Source="{Binding Items}" />
</xcdg:DataGridControl.Resources>
</xcdg:DataGridControl>
</DataTemplate>
</TabControl.ContentTemplate>
I don't use the eXceed Grid so cannot test whether these work - just a couple of ideas to try!
Colin E.
You can use x:Shared="True" attribute on a resource. That means a new instance is created for every use of that resource.
Example:
<UserControl.Resources>
<xcdg:DataGridCollectionViewSource x:Key="GridData"
x:Shared="False"
Source="{Binding Items}" />
</UserControl.Resources>

How to properly bind a ListBoxItem in WPF?

I have a listbox and I want to iterate over a collection of Bars in my Foo-object.
<ListBox DataContext="{Binding Path=Foo.Bars}" >
<ListBox.Items>
<ListBoxItem>
<ContentControl DataContext="{Binding Path=.}" />
</ListBoxItem>
</ListBox.Items>
</ListBox>
This is the datatemplate I want to use.
<DataTemplate DataType="{x:Type Bar}">
<Label Content="hello stackoverflow" />
</DataTemplate>
If I snoop (--> examine by using the tool Snoop) my application, I notice that the entire collection of Bars is bound to the ContentControl, in stead of just 1.
How can I properly bind so the iteration over the collection goes fine?
You can just set the DataTemplate, and WPF does all the work. Set the ItemsSource to a list of Bar items, and then define a DataTemplate for Bar items.
<ListBox ItemsSource="{Binding Path=Foo.Bars}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type Bar}">
<Label Content="hello stackoverflow" />
</DataTemplate>
</ListBox.Resources>
</ListBox>
You could also set the ItemsTemplate directly by using <ListBox.ItemTemplate> instead of <ListBox.Resources>
See Data Binding Overview at MSDN.
First add your namespace to the Window element (Intellisense) :
xmlns:local="clr-namespace:yourenamespace"
Then the following XAML ( in Window.Resources is a clean way to do it ) :
<Window.Resources>
<ObjectDataProvider x:Key="DataProvider" ObjectType="{x:Type local:Foo}"/>
<DataTemplate x:Key="Template" >
<TextBlock Text="{Binding Bar}"/>
</DataTemplate>
</Window.Resources>
Place the Listbox :
<ListBox DataContext="{Binding Source={StaticResource DataProvider}}" ItemsSource="{Binding Bars}" ItemTemplate="DynamicResource Template" />
But, it depends on your code-behind object, you have to set a constructor to initialise public properties within your object which are ObservableCollection<> preferably (There is some restriction rules with object instance in XAML).

Resources