1
votes

Let's say we have the following Models:

public class MyClass : BaseModel
{
    public Item Item1 {get ...; set ...;}
    public Item Item2 {get ...; set ...;}
    public Item Item3 {get ...; set ...;}
    public Item Item4 {get ...; set ...;}
    public ObservableCollection<ItemContainer> AllItemContainers {get ...; set ...;}
}

public class ItemContainer : BaseModel
{
    public Item Item {get ...; set ...;}
    public MyEnum EnumProp {get ...; set ...;}
    public byte? SomeProp {get ...; set ...;}
}

public class Item : BaseModel
{
    public string Name {get ...; set ...;}
    public int Id {get ...; set ...;}
}

All Properties have INPC (implemented in BaseModel).

I had 4 Comboboxes which displayed Item1, Item2, etc. and for the ItemsSource I created a list of Items in my viewmodel which I created from the AllItemContainers property.

var items = new ObservableCollection<Item>();            
foreach (ItemContainer ic in Model.MyClass.AllItemContainers)
{
    items.Add(ic.Item);
}

So far it was working well, the combobox displayed the correct Itemname and I could choose an item from the dropdown and assign it to the Item1 property.

Now I have played around with a multicolumn combobox like so:

<ComboBox Width="150" Margin="5" HorizontalContentAlignment="Stretch"
          ItemsSource="{Binding Model.MyClass.AllItemContainers}">                    
  <ComboBox.ItemTemplate>
    <DataTemplate>
      <TextBlock Margin="2" Text="{Binding Path=Item.Name}"/>
    </DataTemplate>
  </ComboBox.ItemTemplate>
  <ComboBox.ItemContainerStyle>
    <Style TargetType="{x:Type ComboBoxItem}">
      <Setter Property="MinWidth" Value="250"/>
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate>
              <Grid Width="{Binding ActualWidth, RelativeSource={RelativeSource
                    FindAncestor, AncestorType={x:Type ComboBoxItem}}, Mode=OneTime}">
                <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="*"/>
                  <ColumnDefinition Width="*"/>
                  <ColumnDefinition Width="0.5*"/>
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Text="{Binding Path=Item.Name}"/>
                <TextBlock Grid.Column="1" Text="{Binding Path=EnumProp,
                           Converter={StaticResource enumToStringConverter}}"
                           HorizontalAlignment="Center"/>
                <TextBlock Grid.Column="2" Text="{Binding Path=SomeProp}"/>
              </Grid>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>
    </ComboBox.ItemContainerStyle>
  </ComboBox>

This is working well, I see only the name in the textbox of the combobox and I have 3 columns in my dropdown.

But now I obviously can not bind the SelectedItem directly, because the destination of the binding is of type Item where the SelectedItem is of type ItemContainer.

<Combobox SelectedItem="Model.MyClass.Item1"/>

I tried pretty much anything from setting

<Combobox SelectedValuePath="Item"/>

to writing a converter like this

public class ItemContainerToItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var item = value as Item;
        var containers = parameter as ObservableCollection<ItemContainer>;
        if (item != null && containers != null)
            return containers.FirstOrDefault(f => f.Item.Id == item.Id);
        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var container = value as ItemContainer;
        if (container != null)
            return container.Item;
        return null;            
    }
}

I tried to bind the ConverterParameter, but since it isn't a DependencyProperty it could not be bound.

<ComboBox SelectedItem="{Binding Model.MyClass.Item1,
          Converter={StaticResource itemConverter},
          ConverterParameter={Binding Model.MyClass.AllItemContainers}}"/>

I'm running out of ideas now and I don't want to use the SelectionChanged-Event (not even with EventToCommand) and assign the value in my viewmodel and listen to the PropertyChanged event to refresh to combobox.

Is there any other way to accomplish this, or am I missing something?

Help is really appreciated.

1
Is there any specific reason for not using the PropertyChanged event? You'd have saved a lot of time and it'd be more maintainable.0x8BADF00D
@Mostey I just thought that this wouldn't be the mvvm way to do this, I just wanted the use bindings and don't let the viemodel change the item.return developer.Name

1 Answers

0
votes

You can try to set SelectedValuePath and, instead of SelectedItem, bind SelectedValue property to a property of type Item in your Model/ViewModel :

<ComboBox Width="150" Margin="5" HorizontalContentAlignment="Stretch" 
          ItemsSource="{Binding AllItemContainers}" 
          SelectedValuePath="Item"
          SelectedValue="{Binding Item1, Mode=TwoWay}">