wpf combobox multibinding could not get combobox items (programmatically added or bind to database) of relative source combobox in first step

0

I want to disable some items of combobox by specific conditions. For this issue, I used multibinding. If I described all items of combobox in xaml, there is no problem. But I want to populate combobox items programmatically. So in this case , I could not get items, returns null, and throw me out of the program in the first step. my xaml codes are like that:

<Window.Resources>
    <local:TekerDisabler x:Key="tekerDisabler"/>
</Window.Resources>
<Grid>
    <ComboBox x:Name="cbx" HorizontalAlignment="Left" Margin="41,125,0,0" VerticalAlignment="Top" Width="227">
        <ComboBox.ItemContainerStyle>
            <Style TargetType="ComboBoxItem">
                <Setter Property="IsEnabled">
                    <Setter.Value>
                        <MultiBinding Converter="{StaticResource tekerDisabler}">
                            <Binding ElementName="txt1" Path="Text"/>
                            <Binding ElementName="txt2" Path="Text"/>
                            <Binding ElementName="txt3" Path="Text"/>

                            <Binding RelativeSource="{RelativeSource Self}"/>
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </Style>
        </ComboBox.ItemContainerStyle>
    </ComboBox>
    <TextBox x:Name="txt1" HorizontalAlignment="Left" Height="23" Margin="41,38,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="120"/>
    <TextBox x:Name="txt2" HorizontalAlignment="Left" Height="23" Margin="207,38,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="211" TextChanged="txt2_TextChanged"/>
    <TextBox x:Name="txt3" HorizontalAlignment="Left" Height="24" Margin="478,37,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="196"/>

</Grid>

and my c# codes are like that:

namespace App1.Pencereler
{

public partial class deneme : Window
{
    public deneme()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        cbx.Items.Add("0");
        cbx.Items.Add("1");
        cbx.Items.Add("2");
        cbx.Items.Add("3");
    }

    private void txt2_TextChanged(object sender, TextChangedEventArgs e)
    {
        cbx.SelectedIndex = 1;
    }
}
class TekerDisabler : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        bool enable = true;
        var itemler = values[3] as ComboBoxItem;
        if (itemler == null || values[0].ToString() == null || values[1].ToString() == null || values[2].ToString() == null)
        { enable = true; }
        else
        {
            switch (values[0].ToString())
            {
                case "a":
                    switch (values[1].ToString())
                    {
                        case "b":
                            switch (values[2].ToString())
                            {
                                case "c":
                                    switch (itemler.Content.ToString())
                                    {
                                        case "0":
                                        case "2":
                                            enable = false;
                                            break;
                                        default:
                                            enable = true;
                                            break;
                                    }
                                    break;
                                default:
                                    enable = true;
                                    break;
                            }
                            break;
                        default:
                            enable = true;
                            break;
                    }
                    break;
                default:
                    enable = true;
                    break;
            }

        }
        return enable;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

}

For Example, in the first step, I write txt1:a, txt2:b, txt3:d and so all items' display enabled, and then I write txt1:a, txt2:b, txt3:c and contents of combobox (0,2) disabled, there is no problem. But when running program, in the first step I write txt1:a, txt2:b, txt3:c when drop down combobox, program trow me out. How to overcome this problem?

Error Message and details are like that: enter image description here

And Error details are like that:

System.NullReferenceException
  HResult=0x80004003
  İleti=Nesne başvurusu bir nesnenin örneğine ayarlanmadı.
  Kaynak=App1
  StackTrace:
   konum App1.Pencereler.TekerDisabler.Convert(Object[] values, Type targetType, Object parameter, CultureInfo culture) D:\C Sharp\WPF\App1\App1\Pencereler\deneme.xaml.cs içinde: 60. satır
   konum System.Windows.Data.MultiBindingExpression.TransferValue()
   konum System.Windows.Data.MultiBindingExpression.Transfer()
   konum System.Windows.Data.MultiBindingExpression.UpdateTarget(Boolean includeInnerBindings)
   konum System.Windows.Data.MultiBindingExpression.AttachToContext(Boolean lastChance)
   konum System.Windows.Data.MultiBindingExpression.AttachOverride(DependencyObject d, DependencyProperty dp)
   konum System.Windows.Data.BindingExpressionBase.OnAttach(DependencyObject d, DependencyProperty dp)
wpf
combobox
multibinding
asked on Stack Overflow Jun 8, 2020 by halil balcıoğlu • edited Jun 8, 2020 by halil balcıoğlu

2 Answers

1

It would be interesting to know what exact error you get.
I assume the ComboBoxItem.Content returns null. The item containers are rendered (generated) after the ComboBox is opened. Only the data items exist at this moment. So opening the drop down the first time all item containers are null and about to be rendered.

Anyway, the following simplified version of your code will very likely fix your problem:

TekerDisabler.cs

public class TekerDisabler : IMultiValueConverter
{
  #region Implementation of IMultiValueConverter

  public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
  {
    var currentItem = values[0] as string;
    var predicate = "abc";
    string input = string.Concat(values.Skip(1).Cast<string>());
    return !(input.Equals(predicate, StringComparison.Ordinal)
             && (currentItem.Equals("0", StringComparison.Ordinal)
                 || currentItem.Equals("2", StringComparison.Ordinal)));
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
    throw new NotSupportedException();

  #endregion
}

TekerDisabler.cs - Alternative version

public class TekerDisabler : IMultiValueConverter
{
  #region Implementation of IMultiValueConverter

  public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
  {
    var currentItem = values[0] as string;
    var predicate = "abc";
    string input = string.Concat(values.Skip(1).Cast<string>());
    return !(values[1].Equals("a", StringComparison.Ordinal)
             && values[2].Equals("b", StringComparison.Ordinal)
             && values[3].Equals("c", StringComparison.Ordinal)
             && (currentItem.Equals("0", StringComparison.Ordinal)
                 || currentItem.Equals("2", StringComparison.Ordinal)));
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
    throw new NotSupportedException();

  #endregion
}

MainWindow.xamlk.cs

partial class MainWIndow : Window
{
  public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
    "Items",
    typeof(ObservableCollection<string>),
    typeof(MainWindow),
    new PropertyMetadata(default(ObservableCollection<string>)));

  public ObservableCollection<string> Items
  {
    get => (ObservableCollection<string>) GetValue(MainWindow.ResultsProperty);
    set => SetValue(MainWindow.ResultsProperty, value);
  }
}

MainWindow.xaml

<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=main:MainWindow}, Path=Items}">
  <ComboBox.ItemContainerStyle>
    <Style TargetType="ComboBoxItem">
      <Setter Property="IsEnabled">
        <Setter.Value>
          <MultiBinding Converter="{StaticResource CellForegroundMultiValueConverter}">
            <Binding />
            <Binding ElementName="TextBox1" Path="Text" />
            <Binding ElementName="TextBox2" Path="Text" />
            <Binding ElementName="TextBox3" Path="Text" />
          </MultiBinding>
        </Setter.Value>
      </Setter>
    </Style>
  </ComboBox.ItemContainerStyle>
</ComboBox>
<TextBox x:Name="TextBox1" />
<TextBox x:Name="TextBox2" />
<TextBox x:Name="TextBox3" />
answered on Stack Overflow Jun 8, 2020 by BionicCode • edited Jun 8, 2020 by BionicCode
0

I accomlished to overcome my problem by adding

        cbx.IsDropDownOpen = true;
        cbx.IsDropDownOpen = false;

after

cbx.Items.Add("0");
cbx.Items.Add("1");
cbx.Items.Add("2");
cbx.Items.Add("3");

in 'Window_Loaded' event.

answered on Stack Overflow Jun 8, 2020 by halil balcıoğlu

User contributions licensed under CC BY-SA 3.0