【问题标题】:Binding returns RelativeSource object instead the one pointed by the binding绑定返回 RelativeSource 对象,而不是绑定指向的对象
【发布时间】:2020-04-07 13:28:12
【问题描述】:

我必须将DataGrid 中列的Visibility 属性绑定到主用户控件的属性。 DataGrid 在单独的 ResourceDictionary 中写入的 DataTemplate 内。我使用了 this 帖子中描述的 ProxyFramework 元素技巧。 问题是代理元素内的 Binding 返回一个 System.Windows.Data.RelativeSource 类型的对象,而不是我需要的值。

这里是代码示例:

MainWindow.xaml

<Window
    x:Class="TestRelativeSource.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
    xmlns:local="clr-namespace:TestRelativeSource"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Name="WinRoot"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <local:DerivedResource />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <TabControl
            Name="MainTabControl"
            ContentTemplate="{StaticResource MyDataTemplate}"
            ItemsSource="{Binding Path=MyList, ElementName=WinRoot, diag:PresentationTraceSources.TraceLevel=High}">
        </TabControl>
        <Button
            x:Name="button"
            Grid.Column="1"
            Width="72"
            Margin="0,10,0,0"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Click="Button_Click"
            Content="Test" />
        <Button
            x:Name="buttonVisible"
            Grid.Column="1"
            Width="72"
            Margin="0,35,0,0"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Click="ButtonVisible_Click"
            Content="TestVisible" />

    </Grid>
</Window>

背后的代码

namespace TestRelativeSource
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private ObservableCollection<MyCollections> myList;
        public ObservableCollection<MyCollections> MyList
        {
            get { return myList; }
            set { myList = value; OnPropertyChanged(); }
        }

        private bool visible;
        public bool MyVisible
        {
            get { return visible; }
            set { visible = value; OnPropertyChanged(); }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MyList = new ObservableCollection<MyCollections>();
            var coll = new MyCollections();
            coll.Name = "Test";
            coll.Items = new ObservableCollection<MyItem>();
            coll.Items.Add(new MyItem() { Name = "name1", TrueFalse = true });
            coll.Items.Add(new MyItem() { Name = "name2", TrueFalse = false });
            coll.Items.Add(new MyItem() { Name = "name3", TrueFalse = true });
            coll.Items.Add(new MyItem() { Name = "name4", TrueFalse = false });
            MyList.Add(coll);
        }

        private void ButtonVisible_Click(object sender, RoutedEventArgs e)
        {
            MyVisible= !MyVisible;
        }
    }

    public class MyCollections : INotifyPropertyChanged
    {
        private string name;

        public string Name
        {
            get { return name; }
            set { name = value; OnPropertyChanged(); }
        }

        private ObservableCollection<MyItem> myVar;
        public ObservableCollection<MyItem> Items
        {
            get { return myVar; }
            set { myVar = value; OnPropertyChanged(); }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public override string ToString()
        {
            return Name;
        }
    }

    public class MyItem : INotifyPropertyChanged
    {
        private string _name;
        private bool _trueFalse;

        public string Name { get => _name; set { _name = value; OnPropertyChanged(); } }
        public bool TrueFalse { get => _trueFalse; set { _trueFalse = value; OnPropertyChanged(); } }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

DerivedResource.xaml

<ResourceDictionary
    x:Class="TestRelativeSource.DerivedResource"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
    xmlns:local="clr-namespace:TestRelativeSource"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <BooleanToVisibilityConverter x:Key="BoolToVis" />
    <DataTemplate x:Key="MyDataTemplate">
        <DockPanel>
            <DataGrid
                x:Name="MyDataGrid"
                AutoGenerateColumns="False"
                CanUserDeleteRows="False"
                ItemsSource="{Binding Items, Mode=OneWay}">
                <DataGrid.Resources>
************** this binding returns a System.Windows.Data.RelativeSource instead Window***************
                    <FrameworkElement x:Key="ElementTabTypeProxyElement" DataContext="{Binding Source={RelativeSource AncestorType={x:Type Window}}}" />
                </DataGrid.Resources>
                <DataGrid.Columns>
                    <DataGridTextColumn
                        Binding="{Binding Name, Mode=OneWay, FallbackValue='---'}"
                        Header="Name"
                        />
                    <DataGridCheckBoxColumn
                        Binding="{Binding TrueFalse, Mode=OneWay, FallbackValue=false}"
                        Header="TrueFalse"
************Here I want a boolean to convert to visibility from the property MyVisible***************
                        Visibility="{Binding Source={StaticResource ElementTabTypeProxyElement}, Path=DataContext.MyVisible, Converter={StaticResource BoolToVis}, diag:PresentationTraceSources.TraceLevel=High}" />
                </DataGrid.Columns>
            </DataGrid>
        </DockPanel>
    </DataTemplate>

</ResourceDictionary>

我该如何解决这个问题? 谢谢

【问题讨论】:

  • 应该是相对来源:"{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
  • 是的!改变了,用一个虚拟的 ContentControl 移动了 DockPanel 中的代理,现在就像一个魅力!非常感谢

标签: c# wpf xaml binding relativesource


【解决方案1】:

正如 ASh 所说,“{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}”是正确的解决方案,但在 xaml 中也稍作修改:

        <DockPanel>
            <DockPanel.Resources>
                <FrameworkElement x:Key="ElementTabTypeProxyElement" DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=MyVisible, diag:PresentationTraceSources.TraceLevel=High}" />
            </DockPanel.Resources>
            <ContentControl Content="{StaticResource ElementTabTypeProxyElement}" Visibility="Collapsed" />
            <DataGrid
                x:Name="MyDataGrid"
                AutoGenerateColumns="False"
                CanUserDeleteRows="False"
                ItemsSource="{Binding Items, Mode=OneWay}">

                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Name, Mode=OneWay, FallbackValue='---'}" Header="Name" />
                    <DataGridCheckBoxColumn
                        Binding="{Binding TrueFalse, Mode=OneWay, FallbackValue=false}"
                        Header="TrueFalse"
                        Visibility="{Binding Source={StaticResource ElementTabTypeProxyElement}, Path=DataContext, Converter={StaticResource BoolToVis}, diag:PresentationTraceSources.TraceLevel=High}" />
                </DataGrid.Columns>
            </DataGrid>
        </DockPanel>

因为我忘记了在可视化树中插入 ProxyElement 的虚拟 ContentControl

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-12
    • 2013-07-20
    • 2012-07-08
    • 2017-09-28
    • 1970-01-01
    • 2011-06-10
    • 2019-01-20
    • 2019-09-29
    相关资源
    最近更新 更多