WPF MultiBinding in Convertor fails ==> DependencyProperty.UnsetValue
Asked Answered
E

5

16

My code fails at at startup because the values array in the Converter that is called by the Multibinding is not filled with proper value but have a value of DependencyProperty.UnsetValue.

have a look at Convertor and also see where i getting error

public class ButtonColorConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            string val1 = string.Format("  {0}  ", values[0]);
            string val2 = (string)values[1];  **//Here i am getting ==> {DependencyProperty.UnsetValue}** 
            return val1.Equals(val2)
                ? Brushes.NavajoWhite
                : Brushes.White;  
        }

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

You can download full code or see my codes snippet as per below.

MainWindow.xaml

<Window x:Class="DataPager.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:Local="clr-namespace:DataPager.Convertor_For_BackGround">
    <Grid>
        <Grid.Resources>
            <Local:ButtonColorConverter x:Key="currentPageSetter"/>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="36*" />
            <RowDefinition Height="275*" />
        </Grid.RowDefinitions>
        <ItemsControl Name="pageControl" ItemsSource="{Binding Path=PageCollection}" Grid.Row="0">
            <ItemsControl.Template>
                <ControlTemplate TargetType="ItemsControl">
                    <Border >
                        <StackPanel>
                            <ItemsPresenter></ItemsPresenter>
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </ItemsControl.Template>
            <ItemsControl.ItemsPanel x:Uid="pageItemTemplate">
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button x:Name="pageNumberButton" Margin="3,4" Content="{Binding Path=Page_Number}">
                        <Button.Background>
                            <MultiBinding Converter="{StaticResource currentPageSetter}">
                                <Binding Path="Page_Number" />
                                <Binding Path="CurrentPage.Page_Number" /> **//This Binding not resolves properly**
                            </MultiBinding>
                        </Button.Background>
                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>

        </ItemsControl>
        <TextBox Text="{Binding Path=CurrentPage.Page_Number,Mode=TwoWay, FallbackValue=asdf}" Grid.Row="1" Height="23"  Margin="79,62,257,0" Name="textBox1" VerticalAlignment="Top" Width="167" />
    </Grid>
</Window>

MainWindow.xaml.cs

 public partial class MainWindow : Window
    {
        public MainWindow()
        {


            MyPageViewModel = new PageViewModel();

            MyPageViewModel.PageCollection.Add(new PageNumberViewModel(string.Format("  {0}  ",0)));
            MyPageViewModel.PageCollection.Add(new PageNumberViewModel(string.Format("  {0}  ",1)));
            MyPageViewModel.PageCollection.Add(new PageNumberViewModel(string.Format("  {0}  ",2)));
            MyPageViewModel.PageCollection.Add(new PageNumberViewModel(string.Format("  {0}  ",3)));

            InitializeComponent();
        }

        public PageViewModel MyPageViewModel
        {
            get
            {
                return this.DataContext as PageViewModel;
            }
            set
            {
                this.DataContext = value;
            }
        }
    }

And this is ViewModel Classes.

PageViewModel.cs

public class PageViewModel:ViewModelBase
    {
        private ObservableCollection<PageNumberViewModel> m_pageCollection = new ObservableCollection<PageNumberViewModel>();
        private PageNumberViewModel m_currentPage = new PageNumberViewModel(string.Format("  {0}  ",0));

        public PageViewModel()
        {
            m_currentPage = new PageNumberViewModel(string.Format("  {0}  ", 1000));
        }

        public PageNumberViewModel CurrentPage 
        {
            get
            {
                return this.m_currentPage;
            }
            set
            {
                if (m_currentPage == value)
                    return;
                this.m_currentPage = value;
                base.OnPropertyChanged("CurrentPage");
            }
        }
        public ObservableCollection<PageNumberViewModel> PageCollection
        {
            get
            {
                return this.m_pageCollection;
            }
            set
            {
                if (m_pageCollection == value)
                    return;
                this.m_pageCollection = value;
                base.OnPropertyChanged("PageCollection");
            }
        }
    }

PageNumberViewModel.cs

public class PageNumberViewModel : ViewModelBase
    {
        private string m_pageNumber;

        public PageNumberViewModel()              
        {

        }
        public PageNumberViewModel(string pageNumgerArg)
        {
            this.m_pageNumber = pageNumgerArg;
        }

        public string Page_Number
        {
            get { return m_pageNumber; }
            set
            {
                if (m_pageNumber == value)
                    return;
                m_pageNumber = value;
                OnPropertyChanged("PageNumber");
            }
        }

    }
Esmond answered 22/7, 2011 at 6:57 Comment(1)
Even without looking deeper into your code, I would strongly suggest to first test before you execute (string)values[1] (always beware of "magic numbers") if a) values[] != null and b) really contains an object at index 1 (or at least values.Length > 0) , and c) that object really has the desired type STRING. Triggers might fire mre often and then contain different values than you might expect, so always check your Arrays before you use them that directly. That might already help you to prevent your exceptions.Priory
H
12

When you set a list in any ItemsSource, the DataTemplate's DataContext for the individual items will each item of the list.

True that your TextBlock binding is working correctly, because the DataContext is set to your main object: PageViewModel

But in your DataTemplate the DataContext will be set to PageNumberViewModel, since these are the items in your collection.

Therefor, the binding to Path=CurrentPage.Page_Number will result to UnsetValue, because CurrentPage is not a property of PageNumberViewModel

Hope this clarifies things!

If you truly wish to bind to the CurrentPage property of your Window's DataContext, consider using an ElementName binding:

Give window a name, bind to

<Binding ElementName="name" Path="DataContext.CurrentPage.Page_Number" />

or use a RelativeSource binding:

<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}" Path="DataContext.CurrentPage.Page_Number" />
Hotheaded answered 22/7, 2011 at 8:21 Comment(0)
M
9

It looks like you might not have the correct DataContext to resolve the path CurrentPage.Page_Number. A good way of debugging this sort of thing is to remove the path so that you can inspect the DataContext within your value converter:

<Binding Path="." />

Then set a breakpoint in your ButtonColorConverter and have a look at exactly what you are trying to convert.

Mercymerdith answered 22/7, 2011 at 7:24 Comment(3)
You can see in side my code when i bound this CurrentPage.Page_Number to TextBox's Text property it working fine. it only gives exception if we use it in Multibinding with ConvertorEsmond
Yes, but isn't your content binding, Content="{Binding Path=Page_Number}", affecting the DataContext of the Button.Background ?Mercymerdith
Ya @ColinE, when i have degug as per you have suggest...i am getting {DataPager.ViewModel.PageNumberViewModel} as instead of {DataPager.ViewModel.PageViewModel} which i have assigned as DataContext Thanks.....Esmond
E
7

DependencyProperty.UnsetValue is merely a constant on the DependencyProperty class.

You can do something like this :

 if (values[1] == DependencyProperty.UnsetValue)
 {
     return null;  // or default value
 }
Eulaeulachon answered 13/11, 2014 at 7:42 Comment(1)
Thanks for the solution. Sometimes the property of the view model can be null, and in this case the value is UnsetValue. Sometimes it is correct to be null, so I need to convert it to null. This is a very simple solution.Koel
A
6

I also encountered this problem and found a solution in another post (https://mcmap.net/q/748480/-wpf-multibinding-unsetvalue-issue). The key is to use the FallbackValue="" property like this:

<MultiBinding Converter="{StaticResource StringFormatConverter}">
    <Binding Path="ResultValueControl.Min" FallbackValue=""/>
    <Binding Path="Format" />
</MultiBinding>
Archerfish answered 19/12, 2012 at 15:12 Comment(0)
E
0

I have just Changed in MainWindow.xaml as per Mr.Arcturus's suggestion and its working fine.

Thank you so much Mr.Arcturus.

you can see ManiWindow.xaml after chage

<Window x:Class="DataPager.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:Local="clr-namespace:DataPager.Convertor_For_BackGround">
    <Grid>
        <Grid.Resources>
            <Local:ButtonColorConverter x:Key="currentPageSetter"/>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="36*" />
            <RowDefinition Height="275*" />
        </Grid.RowDefinitions>
        <ItemsControl Name="pageControl" ItemsSource="{Binding Path=PageCollection}" Grid.Row="0">
            <ItemsControl.Template>
                <ControlTemplate TargetType="ItemsControl">
                    <Border >
                        <StackPanel>
                            <ItemsPresenter></ItemsPresenter>
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </ItemsControl.Template>
            <ItemsControl.ItemsPanel x:Uid="pageItemTemplate">
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button x:Name="pageNumberButton" Margin="3,4" Content="{Binding Path=Page_Number}">
                        <Button.Background>
                            <MultiBinding Converter="{StaticResource currentPageSetter}">
                                <Binding Path="Page_Number" />
                                **<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}" Path="DataContext.CurrentPage.Page_Number" />** 
                            </MultiBinding>
                        </Button.Background>
                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>

        </ItemsControl>
        <TextBox Text="{Binding Path=CurrentPage.Page_Number,Mode=TwoWay, FallbackValue=asdf}" Grid.Row="1" Height="23"  Margin="79,62,257,0" Name="textBox1" VerticalAlignment="Top" Width="167" />
        <Button Content="Button" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="121,110,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
    </Grid>
</Window>
Esmond answered 22/7, 2011 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.