How to hide/show items in a stack panel? - wpf

I have a wpf-mvvm application.
In that I have follwoing...
<Combo box>
item 1
item 2
</Combo box>
<stack pnel>
<user control 1 />
<user control 1 />
</stack pnel>
If user select "item 1" from combo, i need to display "user control 1"
If user select "item 2" from combo, i need to display "user control 2"
In the viewmodel...I have an IList of those two combobox items.
what is the best way to hid/show items here ?

You can actually remove the StackPanel completely, as you'll only be showing one UserControl at a time.
Once you've done that, you can use the technique described here to bind the ComboBox's value to the visibility of the UserControl. Just set the Visibility to Collapsed for the UserControl that's not chosen.
This allows you to handle this completely in XAML.

There is always one more way to do it :-)
For example, you can do the very simple way: subscribe to SelectionChanged, check which is the currently selected item, and set the visibility of the items-to-be-hidden to collapsed.
There are more advanced ways, but I doubt that they are needed for this simple task. However, with the development of your code you might need to reconsider using this approach.

This demonstrates two simple ways in which you can use a style to change the visibility on elements based on the selection in a combo box. The first style checks the SelectedIndex of the combo box, and the second checks its SelectedValue. I've populated the combo box with string objects in this example, but you can use SelectedValue with any kind of object, so long as you know what its ToString() method returns.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<DockPanel>
<ComboBox x:Name="comboBox" DockPanel.Dock="Top">
<system:String>Item 1</system:String>
<system:String>Item 2</system:String>
</ComboBox>
<TextBlock DockPanel.Dock="Top" Text="This displays if Item 1 is selected">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=comboBox, Path=SelectedIndex}" Value="0">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock DockPanel.Dock="Top" Text="This displays if Item 2 is selected.">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=comboBox, Path=SelectedValue}" Value="Item 2">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DockPanel>
</Page>

Related

FocusElement does not change focus when I set it using a DataTrigger

I am making a window that shows the user multiple options, with each a radiobutton and a textbox.
When opening this window, the focus should be on the textbox corresponding to the radiobutton that is checked when opening the window (this is all preset, no need to worry about this).
It is important that it gets fixed either in xaml or in the code-behind.
I have used a DataTrigger to change the FocusedElement to the right textbox, however it gets overwritten after it is set.
The relevant code is in the DataTrigger below. The color gets changed correctly, however the FocusElement does not.
I have tried all the options that I have found on stackoverflow and google. The issue does not lie in the DataTrigger or the setting of the FocusedElement. I think it lies in the fact that it gets overridden at the end. I have used Snoop to see the changes in the Keyboard.FocusedElement and it does not show any change.
<DataTemplate x:Uid="DataTemplate_1" >
<RadioButton x:Uid="RadioButton_1" GroupName="Options" IsChecked="{Binding IsSelected}" Margin="4,0,0,0" Name="RadioBtn">
<StackPanel x:Uid="StackPanel_1" Orientation="Horizontal" >
<Label x:Uid="Label_1" Visibility="{Binding IsUserInput, Converter={cs:OppositeVisibilityConverter}}" Content="{Binding Description, Mode=OneWay}" />
<Label x:Uid="Label_2" Visibility="{Binding IsUserInput, Converter={cs:VisibilityConverter}}" Content="Anders, nl: " />
<TextBox x:Uid="MemAnders" Visibility="{Binding IsUserInput, Converter={cs:VisibilityConverter}}" Text="{Binding AlternativeText}"
Name="MemAnders" MinWidth="400" IsTabStop="False"
>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=MemAnders}"/>
<Setter Property="Background" Value="LightGoldenrodYellow" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=RadioBtn}"/>
<Setter Property="Background" Value="LightBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel >
</RadioButton >
</DataTemplate >
The textbox corresponding to the checked radiobutton should be focused. Instead another (parent?) object is focused.
Anyone know a workaround for this?
The work-around turned out to be setting focus to the textbox in the Window_Loaded function.
I used an algorithm provided by Find a WPF element inside DataTemplate in the code-behind to find the correct textbox element. Then I did TextBox.Focus(); and that fixed it.

WPF action on LostFocus textbox

I'm working on a WPF application and i have a little problem.
I have 1 ToggleButton and 1 TextBox. When i click on the ToggleButton, the TextBox apears and gets focus. This is good. But now i want that when i click on another textbox or just somewhere else, that the textbox loses his focus and disapears. I tried this with Differnet triggers and setters, but can't get it to work.
My code now:
<ToggleButton x:Name="SearchButton" Width="100" Height="100" BorderThickness="0" Margin="580,0,0,0" Template="{DynamicResource ButtonBaseControlTemplate1}" Panel.ZIndex="1">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsChecked, ElementName=SearchButton}" Value="True" />
<Condition Binding="{Binding Visibility, ElementName=SearchBox}" Value="Visible"/>
</MultiDataTrigger.Conditions>
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=SearchBox}" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton><TextBox x:Name="SearchBox" Margin="100,33,0,34" Visibility="{Binding IsChecked, ElementName=SearchButton, Converter={StaticResource BoolVisibilityConverter}}" Opacity="0" FontSize="24" FontFamily="Arial" Background="{x:Null}" Foreground="#FF7F7F7F" BorderThickness="0">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="TextBox.IsFocused" Value="False">
<Setter Property="ToggleButton.IsChecked" Value="False" TargetName="SearchButton" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
The fact, that the styles in the WPF separated from each other, it's just a bunch of settings setters. We can say, that two different styles for controls, with two different visual trees. So when you're trying to style TextBox to access ToggleButton it does not work, because of its visual tree no ToggleButtons.
In WPF for editing elements in the visual tree, and control in particular, uses a template control or controls placed within view of one Style (but this is usually done with the help of templates, such as DataTemplate, or with ControlTemplate).
I think it will suit you to control the Expander. It already has ToggleButton and content. Example:
XAML
<Expander Header="SearchButton">
<TextBox Text="SearchBox: Opened" Background="Gainsboro" />
</Expander>
Output
To change the view of Expander, you need to change his Style. With it, you can set any form and view of control.
For more information see:
Expander in MSDN
Styling and Templating in MSDN
Customizing the Appearance of an Existing Control by Using a ControlTemplate
Data Templating Overview

WPF Default Value (using Style Trigger) with Binding

I'm new to WPF so please bear with me. I have a ComboBox on my WPF window, the ItemSource property is bound to a List of strings property (Countries) and the SelectedItem is bound to a string property (SelectedCountry). Both of these properties are in the code behind - and I'm setting the DataContext to "this" (i.e. the Window).
The ComboBox xaml is:
<ComboBox Name="CountryComboBox"
VerticalAlignment="Center"
Width="200"
ItemsSource="{Binding Path=Countries, Mode=OneTime}"
SelectedItem="{Binding Path=SelectedCountry, Mode=TwoWay}">
</ComboBox>
I wanted to have a default "- Please Select -" option that is displayed when an item is not selected, therefore I placed the following xaml in App.xaml:
<Style TargetType="ComboBox">
<Style.Triggers>
<Trigger Property="SelectedItem" Value="{x:Null}">
<Setter Property="IsEditable" Value="true" />
<Setter Property="IsReadOnly" Value="true" />
<Setter Property="Text" Value="- Please Select -" />
</Trigger>
</Style.Triggers>
</Style>
When my window is first displayed, the combobox does have the "- Please Select -" text as expected. When I then select a value in the combobox, the SelectedCountry gets populated appropriately, but then when I assign "null" to the SelectedCountry property the combobox still has the same selected country when I'd expect it to go back to "- Please Select -". What am I doing wrong?
Thanks.
It may be a better option not to modify the ComboBox and simply overlay a TextBlock over the ComboBox when the SelectedItem is null.
Just wrap the ComboBox and a TextBlock in a Grid and set a DataTrigger on the TextBlock to check if the SelectedItem is null and toggle its Visibility
Example:
<Grid>
<ComboBox x:Name="combo" ItemsSource="{Binding Countries}" SelectedItem="{Binding SelectedItem}" />
<TextBlock x:Name="textblock" Text="- Please Select -" Margin="5,3,0,0" IsHitTestVisible="False">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem,ElementName=combo}" Value="{x:Null}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
Result:
You need to insert a record into the Countries list that has a value of null and the name " - Please Select - ".
Alternatively I suppose you could extend the ComboBox control and write your own so that you could specify the null vale in the list without having to put a record into the Countries selection.
But of the two, it is much easier to just add a record to Countries.

Wpf Combobox selectedvalue trigger

I am trying to change the visibility with a trigger when a particular value in a combobox is selected, and I got the following XAML
<ItemsControl ItemsSource="{Binding AccessControl.Credentials}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid >
<ComboBox Name="chkFieldType"
SelectedValue="{Binding Path=ValueSourceType,Converter={StaticResource enumstringConv}}"
SelectedValuePath="Tag" SelectionChanged="chkFieldType_SelectionChanged" >
<ComboBoxItem Tag="User">User</ComboBoxItem>
<ComboBoxItem Tag="SessionCredential">Field</ComboBoxItem>
<ComboBoxItem Tag="Inherit">From other Resource</ComboBoxItem>
</ComboBox>
<Border " Visibility="Hidden">
<Border.Resources>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedValue, ElementName=chkFieldType}" Value="Inherit">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Resources>
<ComboBox/>
</Border>
In this case a border. The selected value is "Inherit" of type string but the border remainds hidden.
I ran into the same problem and found that you have to set the visibility property using the style only. So instead of having the initial visibility set with:
<Border Visibility="Hidden">
You should set the initial visibility using the style:
<Style TargetType="....">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
....
</Style.Triggers>
</Style>
(I know it's kinda overdue but I thought maybe someone else might run into the same problem).
Try SelectedItem.Tag or SelectedItem.Content instead of SelectedValue
Set your binding on SelectedValue, not SelectedItem.SelectedValue. The way you currently have it, it is looking for ComboBoxItem.SelectedValue, which doesn't exist
<DataTrigger Value="Inherit"
Binding="{Binding Path=SelectedValue,
Converter={StaticResource enumstringConv},
ElementName=chkFieldType}">
I think it's because you are putting the DataTrigger in Border.Resources.
Try putting the style in the window.resources, with a x:key in order to apply the style to the border.
I think that the border.resources can not access to a control "outside it's own resources context"
SelectedItem and SelectedValue are two seperate properties on the ComboBox.
Since your ComboBoxItems are all strings you can change
<DataTrigger Binding="{Binding Path=SelectedItem.SelectedValue, ElementName=chkFieldType}" Value="Inherit">
to
<DataTrigger Binding="{Binding Path=SelectedItem, ElementName=chkFieldType}" Value="Inherit">
I ended up setting the visibility manually via the code behind when the selectedItem event is fired..

WPF ComboBox that shows nothing selected when disabled (IsEnabled == false)

I'm thinking out different ways to have a WPF ComboBox show blank as if nothing is selected when IsEnabled is set to false. Like always I'm trying to do this without having to redefine the whole control template for the ComboBox which is always a struggle I have with WPF. If anybody has any solutions more elegant than redefining the whole ComboBox control template please let me know.
The reason for what I'm trying to do is I have a CheckBox that represents an "All" option and when checked it disables the ComboBox which is used to pick only a single individual item. If my CheckBox is checked it is sometimes confusing to the users to see a value remaining in the ComboBox since that value has no meaning in that state of the UI.
Another requirement is that the solution cannot modify the SelectedValue, SelectedIndex, or SelectedItem values of the ComboBox since I would like to retain the previuosly selected item in the case that the users unchecks the "All" CheckBox.
Solution based on HCL's answer:
<ComboBox IsEnabled="{Binding ElementName=myCheckBox, Path=IsChecked}"
ItemsSource="{Binding Path=MyItems}"
SelectedValue="{Binding Path=MySelectedItem}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl x:Name="content" Content="{Binding MyItemDescription}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBox}, Path=IsEnabled}"
Value="False">
<Setter TargetName="content"
Property="Visibility"
Value="Hidden" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You can do something with triggers:
Try setting the ItemTemplate to an empty DataTemplate when the box is disabled. This will affect the rendering of the selected item and therefore hide it.
Another simple but not very nice solution would be to set the foreground color to the same as a background color.
I believe you can do this with a Style, rather than redefining the control template. Use a Trigger on the IsEnabled property to set the text shown in the ComboBox. Altering the SelectedItem would be my first approach, but since you don't want to do that, you may find success setting the DisplayMemberPath. Something like this (untested)...
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Trigger.Setters>
<Setter Property="DisplayMemberPath" Value="{x:Null}"/>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
Here's a style that does what you want. It employs a technique that I use all the time: a grid that contains multiple versions of the control, and data triggers that ensure that only one version is visible at any one time.
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<DockPanel>
<CheckBox x:Name="IsActive" DockPanel.Dock="Left"/>
<Grid>
<ComboBox
ItemsSource="{TemplateBinding ItemsSource}"
SelectedItem="{TemplateBinding SelectedItem}"
SelectedIndex="{TemplateBinding SelectedIndex}"
SelectedValue="{TemplateBinding SelectedValue}">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=IsActive, Path=IsChecked}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
<ComboBox>
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=IsActive, Path=IsChecked}" Value="False">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</Grid>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.Style>
This preserves the selected item, selected index, and selected value, just as you want. In fact, it does this a little too well; there's not actually a way of telling that the user deactivated the combo box, since there's no property on ComboBox that exposes this information. I'd probably actually implement this as a custom control derived from ComboBox that exposed the value of the check box as an IsActive property. There are lots of other ways to do it.

Resources