Kirill Khod
Kirill Khod

Reputation: 23

WPF binding to elements in resources

I'm having trouble making ListBox elements stay always visible.

I have ListBox where you can select DataGrid. Displayed name is binded to DataGrid Tag. Selected DataGrid is displayed below. DataGrid names, or rather its Tag is binded to an array from Content class. The problem is that ListBox elements are not visible until you select them, and when you do, only selected item's name is displayed.

enter image description here

When I select second element, first one is no longer displayed and vice versa.

here is my code:

XAML:

<Window x:Class="WpfApp1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="525">
        <Window.Resources>
            <col:ArrayList x:Key="dataGridDef">
                <DataGrid Name="DG1" ItemsSource="{Binding Path=List1}" AutoGenerateColumns="False" 
                          Tag="{Binding Path=GridNames[0]}">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="Grid1Name1" Binding="{Binding Path=Name1}"/>
                    </DataGrid.Columns>
                </DataGrid>
                <DataGrid Name="DG2" ItemsSource="{Binding Path=List2}"  AutoGenerateColumns="False"
                          Tag="{Binding Path=GridNames[1]}">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="Grid2Name1" Binding="{Binding Path=Name1}"/>
                    </DataGrid.Columns>
                </DataGrid>
            </col:ArrayList>
        </Window.Resources>
        <StackPanel>
            <ListBox x:Name="lb" ItemsSource="{StaticResource dataGridDef}" DisplayMemberPath="Tag"
                     SelectedValue="{Binding Path=SelectedGrid}"/>
            <ContentControl Content="{Binding ElementName=lb, Path=SelectedItem}"/>
            <TextBlock Height="100" Text="{Binding Path=Str.Header}"/>
            <TextBlock Text="{Binding Path=SelectedGrid.Tag}"/>
        </StackPanel>
    </Window>

C#:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new Content();
        }
    }

    public class Content
    {
        public ObservableCollection<Asd> List1 { get; set; }
        public ObservableCollection<Asd> List2 { get; set; }
        public DataGrid SelectedGrid { get; set; }

        private string[] _gridName;
        public string[] GridNames { get { return _gridName; } }

        public Content()
        {
            List1 = new ObservableCollection<Asd>();
            List2 = new ObservableCollection<Asd>();
            List1.Add(new Asd { Name1 = "1" });
            List1.Add(new Asd { Name1 = "2" });
            List2.Add(new Asd { Name1 = "a" });
            List2.Add(new Asd { Name1 = "b" });
            _gridName = new string[] { "G1", "G2" };
        }
    }
    public class Asd: INotifyPropertyChanged
    {
        private string _name1;
        public string Name1
        {
            get { return _name1; }
            set
            {
                if (value == _name1) return;
                _name1 = value;
                OnPropertyChanged(nameof(Name1));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        internal void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

Upvotes: 2

Views: 4052

Answers (1)

Mikael Koskinen
Mikael Koskinen

Reputation: 12906

You can fix the issue by defining binding source for your resource.

  1. Give your Window a name. For example x:Name=MyWindow.
  2. Define your datagrids' bindings so that their source is MyWindow:

    Tag="{Binding Path=DataContext.GridNames[0], Source={x:Reference MyWindow}}"

Result:

WPF binding source Here's the complete XAML:

<Window x:Class="WpfApp6.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp6"
    xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525" x:Name="MyWindow">
<Window.Resources>
    <collections:ArrayList x:Key="dataGridDef">
        <DataGrid Name="DG1" ItemsSource="{Binding Path=List1}" AutoGenerateColumns="False" Tag="{Binding Path=DataContext.GridNames[0], Source={x:Reference MyWindow}}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Grid1Name1" Binding="{Binding Path=Name1}"/>
            </DataGrid.Columns>
        </DataGrid>
        <DataGrid Name="DG2" ItemsSource="{Binding Path=List2}"  AutoGenerateColumns="False" Tag="{Binding Path=DataContext.GridNames[1], Source={x:Reference MyWindow}}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Grid2Name1" Binding="{Binding Path=Name1}"/>
            </DataGrid.Columns>
        </DataGrid>
    </collections:ArrayList>
</Window.Resources>
<StackPanel>
    <ListBox x:Name="lb" ItemsSource="{StaticResource dataGridDef}" DisplayMemberPath="Tag" SelectedValue="{Binding Path=SelectedGrid, Mode=TwoWay}"/>
    <ContentControl Content="{Binding ElementName=lb, Path=SelectedItem}"/>
    <TextBlock Height="100" Text="{Binding Path=Str.Header}"/>
    <TextBlock Text="{Binding Path=SelectedGrid.Tag}"/>
</StackPanel>

Upvotes: 6

Related Questions