I have a combobox which I'm setting it's itemsSource after getting it from a database, for some reason the combobox doesn't show the first item.
My comboBox:
<ComboBox Text="Add To Message:" ItemsSource="{Binding Messages}" SelectedValue="{Binding SelectedMessage}" Style="{StaticResource comboStyle}"/>
My style:
<Style TargetType="ComboBox" x:Key="comboStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedItemId}" Value="-1">
<Setter Property="SelectedIndex" Value="1"/>
</DataTrigger>
</Style.Triggers>
</Style>
Instead of setting SelectedIndex you should set SelectedMessage:
// get data from db...
SelectedMessage = Messages.FirstOrDefault();
Related
I defined a Combobox whose SelectedValue is binded to a property on the view model VM.SelectedServiceTypeId
<ComboBox Name="ServiceTypeComboBox"
IsEditable="True"
Grid.Row="1"
Grid.Column="1"
Margin="5"
DisplayMemberPath="ServiceTypeName"
ItemsSource="{Binding ServiceTypes,Mode=TwoWay}"
SelectedValue="{Binding SelectedServiceTypeId, Mode=TwoWay}"
SelectedValuePath="ServiceTypeId"
Loaded="ServiceTypeComboBox_Loaded"
/>
The value is correctly updated when the user selects an item in the dropdown menu, but cause the combobox IsEditable the user is able to type whatever he wants that it's not a value in the ItemSource. In this case the SelectedValue does NOT change.
What I need to do is to enable a button when the SelectedValue is among those in the ItemsSource.
Do you have some hint?
Your wpf combobox has a property "Text" that contains the text input by the user. You will have to write some code to check if the entered text matches anything in your itemssource.
<Button Content="Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ServiceTypeComboBox, Path=SelectedValue}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Is there a way in code to get the the DataContent for the Text Property of the ComboBox defined below?
<ComboBox Height="21" Text="{Binding Path=Field1.Value}">
<ComboBox.Resources>
<Style TargetType="ComboBox">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Field2.Value}" Value="">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Resources>
</ComboBox>
Currently the DataContext of the ComboBox is the user control in which it lives. Which makes sense because I want my Text bound to one property and my DataTrigger to be bound to another property. But I need to get the DataContext that's being bound to for the Text property.
Something like this should do it:
Binding binding = BindingOperations.GetBinding(yourComboBox, ComboBox.TextProperty);
object theDataContext = binding.Source;
let's say we have simple data class:
public class Ex {
public string Prop1 {...} // notify property
public string Prop2 {...} // notify property
}
and an ObservableCollection of objects of this class. I want to have this collection displayed in a ListView with seperated DataTemplated which is distinguished by Ex.Prop2 (if it is null or empty then template01 is used, otherwise template02). This property can be changed in runtime so simple "trick" with ListView.ItemTemplateSelector does not work :(
How to achieve this functionality? Is it possible to achieve it any other way than listening to NotifyPropertyChanged on each object of the collection and than changing manually the template?
Thanks for your help.
Below piece of code which I already have:
<ListView x:Name="lstTerms"
ItemsSource="{Binding Game.Words}"
HorizontalContentAlignment="Stretch"
Grid.IsSharedSizeScope="True">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<!-- checks if element is null or its Prop2 is null or empty. If so, uses NullTemplate -->
<ListView.ItemTemplateSelector>
<local:MySelectTemplate
NormalTemplate="{StaticResource NormalItemTemplate}"
NullTemplate="{StaticResource NullItemTemplate}" />
</ListView.ItemTemplateSelector>
</ListView>
Instead of using a TemplateSelector, you can have a single DataTemplate containing two Grids, which switch visibility dependent on the property values.
Here is an example:
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid Background="LightBlue" Name="normalGrid">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Prop1}" Value="{x:Null}">
<Setter Property="Grid.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding Prop1}"></TextBlock>
</Grid>
<Grid Background="Green" Name="nullGrid">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=normalGrid, Path=Visibility}" Value="Visible">
<Setter Property="Grid.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding Prop2}"></TextBlock>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Obviously you could replace the TextBlock elements with UserControls representing your two DataTemplates.
If you want, you can also remove the need for the bulky Styles by binding Grid.Visibility to a property (named, for example, IsVisible) on your ViewModel and using a VisibilityConverter.
I usually just use a ContentControl which changes its ContentTemplate based on a DataTrigger. DataTriggers respond to the value getting changed, while DataTemplateSelectors do not
<Style x:Key="SomeStyleKey" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Prop2}" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Prop2}" Value="">
<Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
...
<ListView.ItemTemplate>
<DataTemplate>
<ContentControl Style="{StaticResource SomeStyleKey}" />
</DataTemplate>
</ListView.ItemTemplate>
You could also use a Converter that returns String.IsNullOrEmpty(value) if you wanted a single DataTrigger
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..
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.