Binding to container not working, but binding to objects works - silverlight

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.

Related

Expander-like WPF control that only hides empty children

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.

Difference between preview and running program

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?

Why ItemTemplate will make items disappear for ListView?

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?

Prevent DataGrid row from being deleted

I want to protect some rows of a DataGrid to be protected from deletion by the user, although the property CanUserDeleteRows is set to true.
Is there a possibility to protect some rows, hopefully through a databinding or a trigger? The ItemsSource is bound to an ObservableCollection of T.
If you have a property on your bound objects that can be used to determine if the current row can be deleted , like 'IsDeleteEnabled', then you can bind the DataGrid's CanUserDeleteRows property to SelectedItem.IsDeleteEnabled.
For example,
<DataGrid Name="dataGrid1"
CanUserDeleteRows="{Binding ElementName=dataGrid1, Path=SelectedItem.IsDeleteEnabled}"
Never done this with a DataGrid. Usually, when I need to control something like this, I use a ListBox and a DataTemplate with a Grid within to give it the idea of a Grid or a ListView with a GridView in the template because they both give you more control over the interaction.
A shot in the dark, since you're binding, you could use a DataGridTemplateColumn.CellEditingTemplate and make your own Delete button/text which is visible or enabled based off logic within your binding object. Maybe something like this (I didn't test this, but it should be a direction you can head)?
<dg:DataGridTemplateColumn Header="Action">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Text Content="Delete" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
<dg:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ButtonEnabled="{Binding Path=IsDeleteEnabled, Mode=OneWay}" Content="Delete" Command="{Binding Path=DeleteMe}" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellEditingTemplate>
</dg:DataGridTemplateColumn>
Using this method, since the command is bound to the individual object, you would probably have to raise an event your screen's ViewModel handles to remove that row from the ObservableCollection.
Again, not sure if this is the best way, but it's my 10 minute stab at it. So if it's horrible, please don't vote me down too much.

Can I databind to properties of a custom class (instantiated in xaml) that then forms the content of a templated listboxitem

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.

Resources