Reputation: 329
I've built a custom ComboBox which shows as a TextBox when ReadOnly is set:
<local:BoolToVisibilityConverter FalseValue="Hidden" TrueValue="Visible" x:Key="BoolVis" />
<local:BoolToVisibilityConverter FalseValue="Visible" TrueValue="Hidden" x:Key="BoolVisRev" />
<Style TargetType="{x:Type local:ComboBoxG}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ComboBoxG}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ComboBox ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedIndex="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedIndex, Mode=TwoWay}"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsReadOnly, Converter={StaticResource BoolVisRev}}"
IsDropDownOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropDownOpen, Mode=TwoWay}"
IsTabStop="False">
</ComboBox>
<TextBox Text="{TemplateBinding Text}"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsReadOnly, Converter={StaticResource BoolVis}}"
IsTabStop="False">
</TextBox>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It's working fine, other than if I instantiate and set a value all in one go. This doesn't work ...
private void One_OnClick(object sender, RoutedEventArgs e)
{
cmb = new ComboBoxG();
Stack.Children.Add(cmb);
var dict = new Dictionary<int, string> { { 0, "aaa" }, { 1, "bbb" }, { 2, "ccc" }, { 3, "ddd" }, };
cmb.ItemsSource = dict;
cmb.DisplayMemberPath = "Value";
cmb.SelectedValuePath = "Key";
cmb.SelectedValue = 3;
}
... whereas this does ...
private void One_OnClick(object sender, RoutedEventArgs e)
{
cmb = new ComboBoxG();
Stack.Children.Add(cmb);
var dict = new Dictionary<int, string> { { 0, "aaa" }, { 1, "bbb" }, { 2, "ccc" }, { 3, "ddd" }, };
cmb.ItemsSource = dict;
cmb.DisplayMemberPath = "Value";
cmb.SelectedValuePath = "Key";
cmb.Loaded += cmb_Loaded;
}
private void cmb_Loaded(object sender, RoutedEventArgs e)
{
cmb.SelectedValue = 3;
}
So I can work around it but it is making it awkward for some uses of this control. Any suggestions, please?
Upvotes: 3
Views: 1068
Reputation: 4986
I've run into this before as well and so far the only semi-pleasant workaround to do this all in one method is to let the message queue drain through the Loaded stage. Note, I'm not proud of this but WPF does things in stages and sometimes you'd prefer to have everything together. You could put the cmd.SelectedValue = 3 into the delegate I suppose but it's a matter of taste...
cmb = new ComboboxG();
theStack.Children.Add(cmb);
var dict = new Dictionary<int, string> { { 0, "aaa" }, { 1, "bbb" }, { 2, "ccc" }, { 3, "ddd" }, };
cmb.ItemsSource = dict;
cmb.DisplayMemberPath = "Value";
cmb.SelectedValuePath = "Key";
Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Loaded);
cmb.SelectedValue = 3;
Beware that this will allow events to run so be careful of reentrancy!
Upvotes: 1
Reputation: 2233
Have you tried setting the selected index instead?
private void One_OnClick(object sender, RoutedEventArgs e)
{
cmb = new ComboBoxG();
Stack.Children.Add(cmb);
var dict = new Dictionary<int, string> { { 0, "aaa" }, { 1, "bbb" }, { 2, "ccc" }, { 3, "ddd" }, };
cmb.ItemsSource = dict;
cmb.DisplayMemberPath = "Value";
cmb.SelectedValuePath = "Key";
cmb.SelectedIndex = 3;
}
Upvotes: 0