WPF Expander IsExpanded binding - c#

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.

Related

WPF: Clickable button in ItemTemplate of a ComboBoxItem with IsEnabled=false

Trying to set up a WPF ComboBox;
some of its items should not be selectable, so I'm binding IsEnabled to some property of the underlying item.
At the same time, I need to define an ItemTemplate that contains e.g. a Button.
This button needs to be clickable, even if the item is not selectable (worth nothing a click on the button should not select the item as such of course; it will trigger a command performing some background actions, which will eventually make the underlying item selectable)
However, when ComboBoxItem.IsEnabled = false, it seems even the button automatically gets disabled.
Brief example:
<ComboBox ItemsSource="{Binding Items}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled" Value="{Binding CanSelectItem}"/>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<!-- This button isn't clickable when ComboBoxItem.IsEnabled = false .. but it should be! -->
<Button Content="Click me" Command="{Binding SomeCmd}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Is there any way to circumvent this? E.g., set some items as non-selectable, however define a button in the ItemTemplate that remains clickable regardless?
Thanks
When you remove the ComboBox.ItemContainerStyle you can set the IsEnabled property of each element in the DataTemplate
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.IsEnabled}"/>
<Button Content="Click me" Command="{Binding SomeCmd}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
The combobox item will still be enabled but the TextBlock will be disabled.
The binding is just an example. Depends on where your IsEnabled property is. In my example the property is in the viewmodel which is DataContext of your Window.
For future reference, have found another way of doing this, based on this answer:
WPF override IsEnabled from Parent
So basically creating a class derived from button that overrides the default IsEnabled behavior.
Benefit is that this seems to do exactly what I was looking for, but it does change one of WPF's pretty.. default behaviors, so might need to be taken with a bit of care

TabControl - ToggleButtons as TabSeletors (XAML-Only Logic)

Currently I have a set of ToggleButtons.
I would like to show a different Tab of my TabControl depending on which button is checked. Basically the same bahaviour like when a differnet Tab is selected. Not sure if my needs are nonsense but anyways. I want the SelectedTab to change depending on which button is clicked. Moreover my ToggleButtons are RadioButtons stlyed to Togglebuttons (I only want one to be checked at a time). I want to try to achieve my needs only in XAML (if even possible).
So here's part of my XAML:
<Window.Resources>
<sys:Int32 x:Key="CurrentTab"></sys:Int32>
<Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="Aqua"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Background" Value="Transparent"></Setter>
</Style>
</Window.Resources>
<TabControl Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
SelectedIndex="{StaticResource CurrentTab}">
<TabItem Visibility="Collapsed"></TabItem>
<TabItem Visibility="Collapsed"></TabItem>
</TabControl>
What I was thinking of would be something like (pseudoCode):
<Setter Target="{StaticResource CurrentTab}" Value="{ButtonsToolTip}></Setter>
Basically is it even possible to assign values to variables in XAML and if it is - how ?
As an example on why and what I try to achieve is something like this GUI:
You cannot change value of a primitive type declared as resource using xaml. But you can use a property of an object to act as your variable. Eg;
<sys:Int32 x:Key="IntKey">12</sys:Int32>
is non-modifiable using XAML. But, Value property of DictionaryEntry (shown below) is modifiable, despite the fact that like int(IntKey), DEKey is non-modifiable too.
<coll:DictionaryEntry x:Key="DEKey" Key="TagKey" Value="100"/>
If I try to change integer(IntKey) via binding , it won't allow. Eg; <TextBox Text="{Binding Mode=OneWay,Source={StaticResource IntKey}}"/> , Mode must be OneWay. TwoWay, OneWayToSource values are not allowed.
But I can write
<TextBox Text="{Binding Value,Source={StaticResource DEKey}}"/>
and any textbox value will be updated in Value of DictionaryEntry(DEKey). Note, two-way binding won't work as DictionaryEntry is not a DependencyObject. But you can now change your variable (Value property) the way you like. But only concern is : changes to Value property will not be reflected back in bounded control.
Yes, you can make use of above information to show Tabs w.r.t. radiobuttons with approach given below. For binding to work properly both ways, we need a DependencyObject and a DependencyProperty, so we use FrameworkElement and its Tag property.
This Tag property now mimics your Variable in question.
<Window.Resources>
<FrameworkElement x:Key="rbTagHolder" Tag="0"/>
</Window.Resources>
...
<ItemsControl x:Name="RadioButtonList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding TabName}" Tag="{Binding TagValue}" GroupName="Choice">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{DynamicResource rbTagHolder}" PropertyName="Tag" Value="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type RadioButton} }}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
...
<TabControl x:Name="TabCtrl" SelectedIndex="{Binding Tag, Source={StaticResource rbTagHolder}}"> ... </TabControl>
Code-behind
RadioButtonList.ItemsSource = new[] { new { TabName = "Tab1", TagValue = "0" }, new { TabName = "Tab2", TagValue = "1" },
new { TabName = "Tab3", TagValue = "2" }, new { TabName = "Tab4", TagValue = "3" }};
Just in case you don't know how to use Blend Behaviors.
A. Include following namespaces :
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
B. Add references to : Microsoft.Expression.Interactions, System.Windows.Interactivity On my system these are found in
C:\Program Files (x86)\Microsoft SDKs\Expression\Blend.NETFramework\v4.0\Libraries
There is no way to change the value of a StaticResource with an Event trigger in XAML alone. This will have to be done by binding your StaticResource to a ViewModel's property or using code behind.

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

Highlight (select) DataGridRow based on object property in Silverlight

For context, I'm looking to replicate the FeatureDataGrid implemented by ESRI for a map visualization app. Using MVVM, I have bound the map and data to an observable collection of DataPoints. Each DataPoint has an IsSelected property, which is toggled when you change the associated checkbox in the datagrid, or if you click the map graphic that represents it.
I'd like to remove the IsSelected CheckBoxColumn from the datagrid, and instead change the IsSelected property of a DataPoint by simply selecting it in the datagrid.
Selecting something in the datagrid would change IsSelected, which should be reflected on the map automatically by a change in color/size of the graphic. Conversely, selecting objects in the map should make them highlighted in the datagrid.
Is this possible? Where should I begin?
Here are some attempts that did not work:
<sdk:DataGrid x:Name="datagrid" HorizontalAlignment="Left" Width="400" ItemsSource="{Binding Path=Data, Mode=TwoWay}">
<sdk:DataGrid.RowStyle>
<Style TargetType="sdk:DataGridRow">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</sdk:DataGrid.RowStyle>
</sdk:DataGrid>
and
<sdk:DataGrid x:Name="datagrid" HorizontalAlignment="Left" Width="400" ItemsSource="{Binding Path=Data, Mode=TwoWay}" SelectedItem="{Binding IsSelected, Mode=TwoWay}"/>
To be clear, the map is bound to the collection and isn't an important part of this question--I'm simply looking for a way to bind the IsSelected property of a DataPoint to the DataGridRow's highlighted/selected state.
Let me know if more information would be useful.
EDIT
I've found a solution that I believe violates MVVM and adds some (hopefully) unnecessary logic. Perhaps someone more knowledgable with MVVM could comment and convince me otherwise, or confirm my inclination:
Putting this in the code behind:
public MainPage()
{
InitializeComponent();
Messenger.Default.Register<DataPointSelectedMessage>(this, DataPointSelectedChange);
}
private void DataPointSelectedChange(DataPointSelectedMessage msg)
{
if (datagrid.SelectedItems.Contains(msg.Content))
{
datagrid.SelectedItems.Remove(msg.Content);
}
else
{
datagrid.SelectedItems.Add(msg.Content);
}
}
EDIT 2
I'm still new to silverlight. Turns out ListBox does the behavior I'm looking for by default. Here is the related code for anyone who runs into this:
<ListBox ItemsSource="{Binding Path=Data, Mode=TwoWay}" SelectionMode="Multiple">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I would still be interested to get input on implementing this with a datagrid, however. I may end up needing some datagrid features.
Thanks.

ItemsControl DataContext Binding Error

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.

Categories

Resources