-1

I have to bind the Visibility property of a column in a DataGrid to a property of the main usercontrol. The DataGrid is inside a DataTemplate written in a separate ResourceDictionary. I've used the ProxyFramework element trick as described in this post. The problem is that the Binding inside the proxy element returns an object of type System.Windows.Data.RelativeSource and not the value I need.

Here the code example:

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>

Code behind

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>

How can I address this issue?? Thanks

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Fmusso
  • 1
  • 1
  • it should be *Relative*Source: `"{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"` – ASh Apr 07 '20 at 13:41
  • Yes! changed, moved the proxy in the DockPanel with a dummy ContentControl and now works like a charm!!! many thanks – Fmusso Apr 07 '20 at 14:03

1 Answers1

-1

As ASh noted "{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" is the correct solution, but also a slight modification in the 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>

beacause I forgot the dummy ContentControl to insert the ProxyElement in the visualtree

Fmusso
  • 1
  • 1