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?
Related
In my WPF view, I need something similar to an Expander or a TreeView, but instead of completely hiding the content, I only want to hide empty parts, i.e. TextBoxes with null or empty text, or empty ItemCollections.
I thought about using a style with a DataTrigger or set Visibility with a converter, but how would I link that to the parent's setting (e.g. IsExpanded)?
I would like to avoid doing this in the ViewModel, as that would need a property for each section (and I need lots of them), but it's purely visual and therefore IMHO it only belongs to the View.
So I guess the way to go is to use DependencyProperties or write some CustomControls, but I don't have an idea where to start. The XAML of the end result could look something like this:
<CustomExpander Header="Main" CollapseContentIfEmpty="True">
<CustomExpander Header="Section1" CollapseContentIfEmpty="True">
<StackPanel>
<TextBox Text="{Binding SomeString}" />
<TextBox Text="{Binding SomeEmptyString}" />
</StackPanel>
</CustomExpander>
<CustomExpander Header="Section2" CollapseContentIfEmpty="True">
<ListView ItemsSource="{Binding SomeCollectionView}" />
</CustomExpander>
</CustomExpander>
In this example, if CollapseContentIfEmpty is set to true and the CollectionView shows no elements (e.g. due to filters), only the content of SomeString should be visible, along with all the headers. If SomeString is empty, only "Main" should be visible, as now all child CustomExpanders are empty as well.
Setting CollapseContentIfEmpty to false (e.g. via a Button like in Expander) would show all Children again, regardless if they are empty or not.
I thought about using a style with a DataTrigger or set Visibility with a converter, but how would I link that to the parent's setting (e.g. IsExpanded)?
Use a binding with a {RelativeSource}.
In the following example, the TextBlock is invisible unless you set the Tag property of the parent UserControl to true:
<UserControl xmlns:sys="clr-namespace:System;assembly=mscorlib">
<UserControl.Tag>
<sys:Boolean>false</sys:Boolean>
</UserControl.Tag>
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<StackPanel>
<TextBlock Text="text..."
Visibility="{Binding Tag,
RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</UserControl>
You can of course replace the UserControl with a custom control with a custom bool property.
An Expander collapses its entire Content which is different from hiding specific controls in that content.
I was solving Windows Phone 8.1 ListView wobbling problem and had code like below, however, once I add the ItemTemplate, the contents of the List cannot be seen, I'm wondering why and how to fix the problem.
<ListView
Grid.Row="1"
x:Name="ListViewEvents"
Loaded="OnListViewEventsLoaded"
ItemsSource="{Binding xx}"
ItemTemplateSelector="{StaticResource xx}"
ItemContainerStyle="{StaticResource xx}"
IsItemClickEnabled="True">
<ListView.ItemTemplate >
<DataTemplate >
<Grid Width="{Binding ActualWidth, ElementName=EventsListGrid}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Your Width binding is trying to read the ActualWidth property of an element in your XAML named EventsListGrid, but there's no such element in the sample code you've provided. As such, the binding engine is unable to set the Width property on your grid, most likely setting it to some unset/NaN value. At least I can confirm this when setting up a similar case as the one you provided and inspecting in Snoop the ListViewItem containers generated for each item in the test collection bound to the ListView. Perhaps you want to set the element name to ListViewEvents in this case or some other parent element not shown in the example?
I want to set the background color separately for each item in a WPF ListBox. e.g. If I am adding Widgets to the ListBox, I might set the background color for each one based on the type of widget. This must be done in code (not XAML) as I only know what the items are at run time.
I know how to use ItemContainerStyle to set the style for all items, but how do you do it separately for each item?
Yes you do set ItemContainerStyle, using a StyleSelector.
This example at MSDN is exactly what you are looking for.
There are lots of ways to do this.
One is to use a StyleSelector, as loxxy suggests. This is pretty low on my list, because that kind of code is harder to read (well, find) and test than I'd like.
Another is to use a DataTrigger in the style. This is simple, if (and only if) the items all implement a common property that can be used in the trigger. You might be well served by implementing a wrapper class that exposes this common property, and contains the logic that figures out what value to assign to the property based on the object it's wrapping. (Whether or not this is easier than a StyleSelector is certainly arguable.)
If the items are really and truly heterogeneous, you can accomplish the result by using data templates, e.g.:
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Foo}">
<TextBlock Text="{Binding FooText}" Background="Red"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Bar}">
<TextBlock Text="{Binding BarText}" Background="Yellow"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Baz}">
<TextBlock Text="{Binding BazText}" Background="PapayaWhip"/>
</DataTemplate>
</ListBox.Resources>
etc. This would generally be my first choice, but your question doesn't really explain enough about the circumstances to know if it's the right way to go or not.
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 the following Xaml (Silverlight, but it shouldn't matter):
<ListBox x:Name="Results"> ... </ListBox>
<StackPanel DataContext="{Binding ElementName=Results, Path='SelectedItem.Attributes'}">
<TextBlock Text="{Binding ElementName=Results, Path='SelectedItem.Attributes[ID]'}" />
<TextBlock Text="{Binding '[ID]'}" />
</StackPanel>
When I populate the ListBox, the second TextBlock is populated, but the first TextBlock is not. When I select any Item from the Listbox the first TextBlock is populated, but the second doesn't change.
I'm assuming that I'm missing something to tell the StackPanel's DataContext that it needs to refresh any time I change the SelectedItem in my ListBox, but I'm at a loss on what I need to do.
Ideally, I'd like to not have to bind to the whole path for each of my TextBlocks (there are going to be a bunch of them).
Gaah... I found it. Some nitwit (you read.. me) decided that it'd be smart to override the stackpanel's datacontext when I populated the ListBox. Sorry for anyone that was working on this.