WPF中Convertor的MultiBinding失败 ==> DependencyProperty.UnsetValue

15

我的代码在启动时失败,因为由Multibinding调用的Converter中的值数组没有填充正确的值,而是具有DependencyProperty.UnsetValue的值。

请查看Convertor并查看我出错的地方。

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();
        }
    }

您可以从以下链接下载完整代码,或者查看下面的代码片段。

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;
            }
        }
    }

这是 ViewModel 类。

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");
            }
        }

    }

1
即使不深入研究您的代码,我强烈建议在执行(string)values[1]之前先进行测试(始终注意“魔术数字”),如果a)values[] != null并且b)确实包含索引为1的对象(或至少values.Length > 0),并且c)该对象确实具有所需的类型STRING。触发器可能会更频繁地触发,然后包含与您预期不同的值,因此在直接使用它们之前始终检查您的数组。这可能已经帮助您防止异常情况的发生。 - Jens H
5个回答

11
当您将列表设置为任何ItemsSource时,DataTemplate的DataContext对于单个项目的每个项目都将是该列表中的每个项目。
确实,您的TextBlock绑定正在正常工作,因为DataContext已设置为您的主对象:PageViewModel 但是在DataTemplate中,DataContext将被设置为PageNumberViewModel,因为这些是集合中的项目。
因此,对Path = CurrentPage.Page_Number的绑定将导致未设置值,因为CurrentPage不是PageNumberViewModel的属性。
希望这样可以澄清事情!
如果您真的希望绑定到窗口DataContext的CurrentPage属性,请考虑使用ElementName绑定:
给窗口一个名称,然后绑定至
<Binding ElementName="name" Path="DataContext.CurrentPage.Page_Number" />

或者使用RelativeSource绑定:

<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}" Path="DataContext.CurrentPage.Page_Number" />

9

看起来你可能没有正确的DataContext来解析路径CurrentPage.Page_Number。调试这种问题的好方法是删除路径,以便你可以检查值转换器中的DataContext:

<Binding Path="." />

然后在您的ButtonColorConverter中设置一个断点,查看您要转换的内容。


你可以在我的代码中看到,当我将 CurrentPage.Page_Number 绑定到 TextBoxText 属性时,它能够正常工作。只有在使用多绑定和转换器时才会出现异常。 - Aryan SuryaWansi
1
是的,但是你的内容绑定 Content="{Binding Path=Page_Number}" 是否会影响到 Button.Background 的 DataContext 呢? - ColinE
嗨,@ColinE,按照您的建议进行调试后,我得到的是{DataPager.ViewModel.PageNumberViewModel}而不是我分配为DataContext{DataPager.ViewModel.PageViewModel}。谢谢。 - Aryan SuryaWansi

7

DependencyProperty.UnsetValue仅是DependencyProperty类上的一个常量。

你可以像这样做:

 if (values[1] == DependencyProperty.UnsetValue)
 {
     return null;  // or default value
 }

1
感谢您提供的解决方案。有时候视图模型的属性可能为空,此时的值为UnsetValue。有时候为空是正确的,所以我需要将其转换为null。这是一个非常简单的解决方案。 - Álvaro García

6

我也遇到了这个问题,并在另一篇帖子中找到了解决方法(https://dev59.com/lk7Sa4cB1Zd3GeqP6LwF#3139397)。关键是要像这样使用FallbackValue = ""属性:

<MultiBinding Converter="{StaticResource StringFormatConverter}">
    <Binding Path="ResultValueControl.Min" FallbackValue=""/>
    <Binding Path="Format" />
</MultiBinding>

0

我刚按照Arcturus先生的建议在MainWindow.xaml中进行了更改,现在它可以正常工作。

非常感谢您,Arcturus先生

您可以查看更改后的ManiWindow.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 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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接