Binding возвращает объект RelativeSource вместо объекта, на который указывает привязка - PullRequest
0 голосов
/ 07 апреля 2020

Мне нужно связать свойство Visibility столбца в DataGrid со свойством основного usercontrol. DataGrid находится внутри DataTemplate, написанного отдельным ResourceDictionary. Я использовал трюк с элементом 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>

Как мне решить эту проблему проблема ?? Спасибо

1 Ответ

0 голосов
/ 07 апреля 2020

Как отмечает A Sh, "{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>

потому что я забыл фиктивный ContentControl для вставки ProxyElement в визуальное дерево

...