ItemsControl DataContext Binding Error - c#

I have already looked into this solution: Show if ItemsControl.ItemsSource is null.
I set the DataContext of the ItemsControl via codebehind to an ObservableCollection. Everything works fine except that it only resolves once during the loading phase. If the items control has a few items in the start, the text disappears but doesn't appear later onwards. If it's empty, the text appears, but it doens't go away when i add items later on. I have tried ItemsSource as well but no luck. I'm aware im using a control template as of now, and i can use relative source TemplatedParent but i just wanted to make sure. Upon further testing, the converter function doesn't seem to activate after i try to add/remove items in the list even though the items show on my itemscontrol.
<ItemsControl x:Name="MedicationList" ItemTemplate="{StaticResource UserTemplate}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Grid>
<TextBlock Text="No Items to Display" Visibility="{Binding DataContext, ElementName=MedicationList, Converter={StaticResource AnyItemsToVisibilityConverter}}" />
<ItemsPresenter />
</Grid>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>

What are you using as the datacontext/itemssource? If it is an ObservableCollection as I would expect, then you would be best off binding to its "Count" property and then using a trigger to collapse the text block when necessary.
The reason that the binding isn't currently updating is that the DataContext itself isn't actually changing. Properties on the DataContext ARE changing, so if you bind to the correct property (count) your bindings will update.
This code snippet should work:
<ControlTemplate TargetType="ItemsControl">
<Grid>
<TextBlock x:Name="txtBlock" Text="No Items to Display" Visibility="Collapsed" />
<ItemsPresenter />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Count}" Value="0">
<Setter TargetName="txtBlock" Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
By using a data trigger you can avoid the need for a converter to convert a numeric value into a visibility and keep everything in your .xaml.

Related

Selecting null item in a ListBox

I have a ListBox with an ItemsSource that can contain null values.
I can't select those null values in the ListBox with the mouse, but I can with the keyboard.
Is there any way to make null items selectable by mouse?
Example xaml:
<ListBox>
<ListBox.Items>
<x:Null />
<system:String>Hello</system:String>
<x:Null />
</ListBox.Items>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text">
<Setter.Value>
<Binding />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=.}" Value="{x:Null}">
<Setter Property="Text" Value="Null value!" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Which results in:
however, I can't select any of the Null value! entries using the mouse, which is what I want to do.
If it can be done with another pure-xaml solution, that'd be fine. I'd prefer to not have to use any converters, if possible.
I'm afraid you will have to go for converters (don't really know why you don't want some ?)
From this SO question :
The null "item" is not being selected by the keyboard at all - rather
the previous item is being unselected and no subsequent item is (able
to be) selected.
In short, you can neither select nor deselect a null item in a
ComboBox. When you think you are doing so, you are rather deselecting
or selecting the previous or a new item.
This can perhaps best be seen by adding a background to the items in
the ComboBox. You will notice the colored background in the ComboBox
when you select "Hello", but when you deselect it via the keyboard,
the background color disappears. We know this is not the null item,
because the null item actually has the background color when we drop
the list down via the mouse!
The following XAML, modified from that in the original question, will
put a LightBlue background behind the items so you can see this
behavior.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox x:Name="bars" Height="21" SelectedItem="{Binding Bar}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Background="LightBlue" Width="200" Height="20">
<TextBlock Text="{Binding Name}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel> </Window> ```
If you want further validation, you can handle the SelectionChanged event on the ComboBox and see that
"selecting the null item" actually gives an empty array of AddedItems
in its SelectionChangedEventArgs, and "deselecting the null item by
selecting 'Hello' with the mouse" gives an empty array of
RemovedItems.

WPF datagrid how to get access to the row click

Seems that a WPF application I inherited has a DataGrid
<DataGrid x:Name="dataGrid" ItemsSource="{Binding AllTroubleCalls}" SelectedIndex="{Binding SelectedIndex}"
I want to end up setting the Background color to yellow only if the Textbox contains text in it.
The text appears when I click on certain rows in the Datagrid
It seems that everything is based upon this "Binding"
{Binding ... }
I have a textbox that I added a Name to it
<TextBox ToolTipService.ShowDuration="120000" ToolTip="{Binding ThreatText}" Name="txtThreat" Text="{Binding ThreatText}"
TextWrapping="Wrap" AcceptsReturn="True"
VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible"
Margin="3" Grid.Row="8" Grid.Column="1"
Grid.ColumnSpan="3" Grid.RowSpan="1" IsReadOnly="True" Height="30"/>
Show then when I am in a method I can "test" this and it works
txtThreat.Background = new SolidColorBrush(Colors.Yellow);
However, I'm not understanding why I cannot get a handle on the data changing from whatever row is clicked on in the Datagrid, that data then appears "magically" in many textboxes etc.. on the xaml page.
I gather that the "Binding" is handling this, but it is also MVVM 2 way binding ?
I have tried plastering so many breakpoints into so many methods but I can't seem to get any of them to show me how the data is changing on row click
There's a little too much going on in this question without the details. However, I can at least answer what I think is your goal. The easiest way to get a textbox to be yellow if there's a text in it is with a style:
<TextBox >
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Yellow"></Setter>
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background" Value="White"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
As for figuring out how the data is changing... The Text displayed in your TextBox is bound to ThreatText. That means the text should change only when a property named ThreatText is changed in a class somewhere (your viewmodel). Have you tried putting a breakpoint on the setter for the ThreatText property? You can also put a breakpoint on the getter as well. It sounds like that when you click in the textbox/row, WPF updates the text in the UI, which means it's reevaluating the binding due to some change in ThreatText; this also means it'll hit the getter... you can check out the stack trace if it does to see what's going on.

Hide checkbox, but show its content

Is it possible to hide a checkbox, but leave its content visible?
<ListBox
ItemsSource ="{Binding MyItemCollection}"
SelectionMode="Single"
Width="300"
Height="320">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}">
<CheckBox.Content>
<TextBlock Text="{Binding Item.Code}"/>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel>
<CheckBox Content="Edit Mode"
IsChecked="{Binding Path=EditModeSelected, Mode=TwoWay}">
</CheckBox>
</StackPanel>
I would like to hide the checkboxes in the list box when I turn Edit Mode off (so it should be binded to EditModeSelected), but the text should be left visible.
In order to do so You can keep two TextBlocks. In edit mode visible CheckBox and hide TextBlock and in reader mode vice versa. I hope this may help. As DataTemplate can have only one child here's the fix
Create a Window Resource like below. Two Data Templates were created one for edit mode and another for Reader Mode.
<Window.Resources>
<DataTemplate x:Key="EditModeTemplate">
<CheckBox IsChecked="{Binding IsChecked}">
<CheckBox.Content>
<TextBlock Text="{Binding Item.Code}"/>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="ReaderModeTemplate">
<TextBlock Text="{Binding Item.Code}"/>
</DataTemplate>
</Window.Resources>
Now in .cs file assign the Date Template as per requirements.
if (EditMode)
{
DemoCollection.ItemTemplate = this.Resources["EditModeTemplate"] as DataTemplate;
}
else
{
DemoCollection.ItemTemplate = this.Resources["ReaderModeTemplate"] as DataTemplate;
}
3 possible solutions come in my mind - two of them more or less "hacks" and one more or less clean solution:
A checkbox and textblock for every item - you can get problems with margins etc
A checkbox with no content (which is only visible when in edit mode), and a textblock which is always visible
Take the default controltemplate for checkbox (Default ControlTemplate for CheckBox) and bind the visibility of the checkbox
Here is a Xaml only solution pulled from a project I am working on. In this case "ShowCheck" is a field in the current binding context saying whether or not to show the check.
<CheckBox Content="{Binding Name}">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowCheck}" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<ContentControl Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Basically if the checkbox should be invisible, then I use a style and a trigger to change the checkbox's template to something without the checkbox. My implementation the content is just a string, so this works. If you were putting more complicated objects into the checkbox, you might need to shuttle the ContentTemplate, ContentTemplateSelector, and related fields into the ContentControl that is used to replace the checkbox

Window with ListBox is not displaying error on the ListBox

I have a dialog in my WPF application which contains a ListBox. The ListBox uses the following DataTemplate to display its contents:
<DataTemplate x:Key="AlarmClassTemplate">
<CheckBox Content="{Binding Path=Value}"
IsChecked="{Binding IsChecked}" />
</DataTemplate>
I've also configured the following template and style to display when there is an error in the ListBox's contents:
<ControlTemplate x:Key="InputErrorTemplateA">
<DockPanel LastChildFill="True">
<Image DockPanel.Dock="Right"
Height="30"
Margin="5"
Source="{StaticResource ErrorImage}"
ToolTip="Contains invalid data"
VerticalAlignment="Center"
Width="30" />
<Border BorderBrush="Red"
BorderThickness="5"
Margin="5">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource InputErrorTemplateA}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
And here's the XAML for the ListBox itself:
<ListBox FontSize="20"
FontWeight="Bold"
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="1"
Height="158"
ItemsSource="{Binding Path=IDs, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
ItemTemplate="{StaticResource AlarmClassTemplate}"
Margin="5,0,110,0"
Name="AlarmClassListBox"
ToolTip="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"
Visibility="{Binding Converter={StaticResource BoolToVisibility}, Path=DataTypeIsAlarms}" />
The validation logic for the data in the ListBox is that at least one item has to be checked off. If none of them are, the ListBox should display an error and the OK button on the dialog should be disabled.
The good news is that the OK button on the dialog is indeed disabled when nothing in the ListBox is checked. The bad news is that the Style doesn't seem to be working, in that no red border is displayed around the ListBox and the error image (a red circle with a white exclamation point inside) does not show.
I'm using the same exact ControlTempate and a similar Style on other controls on the same dialog and they work fine. What am I doing wrong? Is it the ListBox? Does ListBox validation work differently?
Indeed the problem is you weren't raising PropertyChanged event for your validation to gets fired.
But i can see one more issue in your code. You have set local value for tooltip on ListBox here:
ToolTip="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"
But you want different tooltip in case validation returns some error which you define in style triggers.
But, local value has higher precedence order than style triggers. So, your tooltip will never be set. So, you should move the tooltip to style setters to work:
<Setter Property="ToolTip"
Value="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"/>
MSDN link - Dependency property value precedence.
I found the answer to my problem in this post. It turns out that I have to raise the PropertyChanged event when the checkboxes change in order for the Validation logic to fire. Since the items in the ListBox implement INotifyPropertyChanged, it was easy to add an event listener for each item as it's added to the ListBox which raises the necessary event.
Thanks anyway.

WPF Expander IsExpanded binding

I have an Expander control with its IsExpanded property bound to a bool in the mvvm model. The binding works fine until you dont touch the expander. Once you click the arrow in the expander to expand, the binding stops working. Setting the bool ShowPreview to false in the model doesn't collapse the expander.
<Expander Name="pExpander"
IsExpanded="{Binding Path=ShowPreview,Mode=OneWay}"
Header="Preview">
<TextBlock Text="{Binding Path=Message, Mode=OneWay}"></TextBlock>
</Expander>
If you remove Mode=OneWay does that fix the problem?
Upon reading your other CTQ (changes to the GUI do not affect the model), I don't have a good suggestion for how to limit the change being seen by the underlying data. What is the difference in:
myModel.MyProperty = true; // in *your* code behind
And
myModel.MyProperty = true; // done by a binding
What caught me out here is that IsExpanded is OneWay by default, so
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Expanded}"/>
</Style>
doesn't work the way I expected. Only if you add Mode=TwoWay, then it works (i.e. the item starts paying attention to my Expanded property, and updating it), as in
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Expanded, Mode=TwoWay}"/>
</Style>
With Silverlight I do this:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
<Expander Name="pExpander" IsExpanded="True" Header="Preview">
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding ShowPreview, Mode=OneWay}">
<ei:ChangePropertyAction PropertyName="IsExpanded" Value="{Binding ShowPreview, Mode=OneWay}"/>
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
<TextBlock Text="{Binding Path=Message, Mode=OneWay}"></TextBlock>
</Expander>
<Expander Name="pExpander1" IsExpanded="True" Header="Preview 1">
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding ShowPreview, Mode=OneWay}">
<ei:ChangePropertyAction PropertyName="IsExpanded" Value="{Binding ShowPreview, Mode=OneWay}"/>
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
<TextBlock Text="{Binding Path=Message1, Mode=OneWay}"></TextBlock>
</Expander>
//...
The binding is not lost when you manualy expand/collapse one Expander...
Do three things,
Make sure your ViewModel is implementing INotifyPropertyChanged. Your ui wont know about the change if your view model doesnt inform it when the property changes
Change the Mode to TwoWay, you want your view model updated when the expander changes and you want your expander updated when the view model changes
Lastly if the above two don't work use a debug converter to ascertain if your binding is failing. there is an example here of how to do this. This is a technique every wpf developer needs.
I know there was an issue with radio buttons that they would lose their bindings when another button in the group was set, i don't think that is the issue here, however a debug converter will help you figure this out.

Categories

Resources