Question

So, here's my scenario: I have a ComboBox where the itemssource is a Dictionary<int, string> of various titles and their IDs. These titles can be disabled at some point in the future, and therefore shouldn't show in the ComboBox anymore. However, when viewing an old item, I still need to be able to show this old value in addition to the current active titles. I'll try to visualize a little better below.

Today:

The ComboBox items consist of

  • 1, Title1
  • 2, Title2
  • 3, Title3

Title3 is selected and the ID (3) is stored.

Tomorrow:

Title3 is disabled and Title4 is added, so the items now consist of

  • 1, Title1
  • 2, Title2
  • 4, Title4

However, if our value from yesterday is the value we're binding (ID 3), there is no matching item. Ideally, I'd like to append our old item to the end like so:

  • 1, Title1,
  • 2, Title2
  • 4, Title4
  • 3, Title3

There would obviously be separate lists for Enabled and Disabled titles, and any item that doesn't bind properly could then reference the Disabled titles variable.

I have investigated fallbackValues and even PriorityBindings but can't seem to find a way to make them fit what I'm trying to do. Perhaps some sort of Converter used with a fallbackValue? I appreciate the help and feedback.

Also, for reference, this is the code I'm currently working with (I'm looking to do this in a datagrid).

WPF:

<DataGridTemplateColumn x:Name="tkTitle" Header="Title" Width="150">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox ItemsSource="{Binding DataContext.TaskTitles, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}" SelectedValue="{Binding Path=tkttID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Key" DisplayMemberPath="Value" Width="Auto" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Associated code in the ViewModel:

public Dictionary<int, string> TaskTitles
{
    get
    {
        return BestClass.taskTitles;
    }
}

EDIT - Working Code Here is the code I used to get everything working. The ItemsSource was updated to a MultiBinding with a Converter. The MultiBinding contains the Active TaskTitles, All TaskTitles, and the ID. These all get passed to the converter so the ID is checked in the active list, and added if it's not active. Thanks to all for the help!

WPF

<DataGridTemplateColumn x:Name="tkTitle" Header="Title" Width="150">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox SelectedValue="{Binding Path=tkttID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Key" DisplayMemberPath="Value" Width="Auto">
                <ComboBox.ItemsSource>
                    <MultiBinding Converter="{StaticResource CheckDisabledTaskTitle}" Mode="OneWay">
                        <MultiBinding.Bindings>
                            <Binding Path="DataContext.TaskTitles" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}" />
                            <Binding Path="DataContext.AllTaskTitles" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}" />
                            <Binding Path="tkttID" />
                        </MultiBinding.Bindings>
                    </MultiBinding>
                </ComboBox.ItemsSource>
            </ComboBox>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Codebehind for Converter

class CheckDisabledTaskTitle : IMultiValueConverter
{
    //values[0] - Active values
    //values[1] - All values (including disabled)
    //values[2] - Current ID
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        //Check to see if the active values contain our current ID
        if (((Dictionary<int, string>)values[0]).ContainsKey((int)values[2]))
        {
            //They do, so return just the active values
            return values[0];
        }
        else
        {
            //They don't, so we'll add only our disabled value to the active list
            ((Dictionary<int, string>)values[0]).Add((int)values[2], ((Dictionary<int, string>)values[1])[((int)values[2])]);
            //Then give that back
            return values[0];
        }
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
Was it helpful?

Solution

If I understand what you're after here...

1) I believe you should modify things in your view-model - and supply a default if the result is null or empty (you need to implement your INotifyPropertyChanged to notify of changes there etc.).

2) The other option would be some sort of converter - which takes your list property and use MultiBinding.

For details see this post of mine (mostly the code there) - refreshing Value Converter on INotifyPropertyChanged

Basically you bind both your proper property list - and the 'default list' - and inside your converter you can make decisions what to do.

e.g.:

<ComboBox  ...>
    <ComboBox.ItemsSource>
        <MultiBinding Converter="{StaticResource ImageConverter}" Mode="OneWay">
            <MultiBinding.Bindings>
                <Binding Path="TaskTitles" />
                <Binding Path="FallbackTitles" />
                <!--Binding Path="" /-->
            </MultiBinding.Bindings>
        </MultiBinding>
    </ComboBox.ItemsSource>
</ComboBox>

OTHER TIPS

I think you need to modify your collection in the ViewModel, so that you...

  1. Load Allowed Values (TaskTitles)
  2. Check for selected value (tkttID) in allowed values. If it does not exist, then add it.

To do this, you should give TaskTitles an auto property with a setter so that you can manage its contents somewhere else..

e.g... (below untested as i have no VS atm). I have also used Linq below, which is .NET 3 and above i think.

public Dictionary<int, string> TaskTitles {get;set;}

public void Initialise()
{
TaskTitles = new Dictionary<int, string>();

foreach(var taskTitle in BestClass.taskTitles)
    TaskTitles.Add(taskTitle);

if(!TaskTitles.Any(t => t.Value == tkttid.Value))
    TaskTitles.Add(tkttid);



}

Note that you are not adding to BestClass.taskTitles, so this collection will remain unaffected. You are creating a new collection and adding the same values in, plus (potentially) one other value.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top