How to create a grid with another one overlapping it and show certain controls of the first grid in the second [closed]

Even with the edit, the question isn’t entirely clear. In particular, your code example doesn’t provide any actual values for the UI elements being displayed, rendering any change in visibility moot. Who cares what’s visible when, if there’s no data?

That said, from your use of the Click event as a way to respond to user input, I suspect that you similarly have some code somewhere that is explicitly setting the Text property for the named UI elements you’re dealing with. This has led you to a desire to reuse the <TextBox Name="Common"/> element, so as to not duplicate code.

If that inference is correct, or even close to being correct, then…the motivation is honorable, but you’ve painted yourself into a corner through your improper use of the WPF API. Specifically, you should view the UI elements as throw-away objects, and keep all the interesting bits of your program in non-UI objects called “view models”. See “MVVM” as a programming paradigm.

By “throw-away”, I mean these objects are created as needed by the framework to serve the purposes of the current state of the UI. They should not take any more significant role than that. When I look at the code example you posted, there are at least a couple of major warning signs in the code: the elements have names, and there is code-behind that is manipulating their visual state.

Both of these characteristics are almost never needed in well-written WPF code. The XAML can very often completely describe not just the appearance of the UI but how it changes visual state based on the operation of the program.

Okay, so with that exposition out of the way, how to implement your code so that it better-suits the WPF API, while at the same time has minimal repetition?

I can see at least a couple of ways immediately. One is to preserve substantially the arrangement of the XAML as you’ve got it now, but move the important elements into a proper view model data structure. Another is to use data templates and multiple view model data structures to automatically update the UI according to what data is active at the moment. I’ll show both approaches here.

Approach #1:

The first step is to create the view model. IMHO, a WPF program will almost always start with the view model, because the XAML (the user interface) exists to serve the view model (the program data), rather than the other way around. The view model should ideally not have any dependencies on the UI framework. It represents the state of your program independent of things specific to the framework, though it will often still have state that represents conditional aspects of the UI itself.

In some cases, you’ll find that you choose to use the view model as an adapter between an even more-rigorous model data structure; this adds a new layer to the program, allowing for the model data structure to be entirely independent of UI concerns entirely. I didn’t bother with that for this example.

class ViewModel1 : NotifyPropertyChangedBase
{
    private string _commonText = "default common text view model 1";
    public string CommonText
    {
        get => _commonText;
        set => _UpdateField(ref _commonText, value);
    }

    private string _uniqueText1 = "default unique text #1";
    public string UniqueText1
    {
        get => _uniqueText1;
        set => _UpdateField(ref _uniqueText1, value);
    }

    private string _uniqueText2 = "default unique text #2";
    public string UniqueText2
    {
        get => _uniqueText2;
        set => _UpdateField(ref _uniqueText2, value);
    }

    private int _gridToShow;
    public int GridToShow
    {
        get => _gridToShow;
        set => _UpdateField(ref _gridToShow, value);
    }

    public ICommand SetGridToShowCommand { get; }

    public ViewModel1()
    {
        SetGridToShowCommand = new SetGridToShow(this);
    }

    private class SetGridToShow : ICommand
    {
        private readonly ViewModel1 _owner;

        public SetGridToShow(ViewModel1 owner)
        {
            _owner = owner;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter) => true;

        public void Execute(object parameter)
        {
            if (parameter is int index ||
                (parameter is string text && int.TryParse(text, out index)))
            {
                _owner.GridToShow = index;
            }
        }
    }
}

This class has a few features typical of WPF view models:

  1. It inherits a base class that does the actual work of implementing INotifyPropertyChanged.
  2. It has public properties that represent the current state of the program, and which will be used in bindings declared in the XAML, either to display a particular value or to control some particular state of the UI.
  3. It has public properties (well, one in this case) for commands to react to user input. In this particular example, the implementation of the single command is a standalone nested class, but in a real-world program this would typically be generalized as well, using helper classes that do things like handling type conversion for command parameters and accepting delegates for the actual implementation of a command.

In this example, the view model includes three string properties, one that represents the shared value between the two UI states, and then two more, each of which being the “unique” value for each state, an int property that represents the current UI state, and an ICommand property that handles the user input.

With that view model declared, now we can look at the XAML:

<DockPanel>
  <DockPanel.DataContext>
    <l:ViewModel1/>
  </DockPanel.DataContext>
  <ToolBarTray DockPanel.Dock="Top">
    <ToolBar>
      <Button Content="Show Grid 1" Command="{Binding SetGridToShowCommand}" CommandParameter="1"/>
      <Button Content="Show Grid 2" Command="{Binding SetGridToShowCommand}" CommandParameter="2"/>
    </ToolBar>
  </ToolBarTray>
  <Grid>
    <StackPanel>
      <StackPanel.Style>
        <Style TargetType="StackPanel">
          <Setter Property="Visibility" Value="Collapsed"/>
          <Style.Triggers>
            <DataTrigger Binding="{Binding GridToShow}" Value="1">
              <Setter Property="Visibility" Value="Visible"/>
            </DataTrigger>
          </Style.Triggers>
        </Style>
      </StackPanel.Style>
      <TextBox Text="{Binding CommonText}"/>
      <TextBox Text="{Binding UniqueText1}"/>
    </StackPanel>
    <StackPanel>
      <StackPanel.Style>
        <Style TargetType="StackPanel">
          <Setter Property="Visibility" Value="Collapsed"/>
          <Style.Triggers>
            <DataTrigger Binding="{Binding GridToShow}" Value="2">
              <Setter Property="Visibility" Value="Visible"/>
            </DataTrigger>
          </Style.Triggers>
        </Style>
      </StackPanel.Style>
      <TextBox Text="{Binding CommonText}"/>
      <TextBox Text="{Binding UniqueText2}"/>
    </StackPanel>
  </Grid>
</DockPanel>

The important parts in the above, relative to your question, are:

  1. Most important, the CommonText property is bound to two different TextBox elements. I.e. the XAML element is not shared (which would have been the literal answer to your question), but rather the underlying view model property is shared. This allows the UI to interact with the user in whatever manner is appropriate for the given UI state, while only have a single state in the view model that represents the user’s input.
  2. The view model object is set as the data context for this part of the visual tree, via the DockPanel.DataContext element binding.
  3. The user input isn’t implemented with handlers for the Click event, but rather via the ICommand that updates the view model state according to the input.
  4. The UI state itself responds to the changes in the view model via DataTrigger elements provided in the Style elements set for each “grid” (I used a StackPanel instead of a Grid in this example, because it was more convenient, but the same general ideas apply regardless.)

Approach #2:

That example alone I think sufficiently addresses the scenario you describe. However, WPF also can display an entirely different configuration of UI elements for a given data context object, through the mechanism of data templates. If we apply that idea to your question, we can:

  1. Establish a couple more view model objects to represent the “unique” values in the program.
  2. Declare a template for each of the view model objects.
  3. Instead of using DataTrigger to change the visual state of the UI, let WPF automatically update the state via the templates, by simply updating the current view model being displayed.

In this scheme, here are the view model objects I came up with…

The main one:

class ViewModel2 : NotifyPropertyChangedBase
{
    private readonly ViewModel2A _viewModel2A = new ViewModel2A();
    private readonly ViewModel2B _viewModel2B = new ViewModel2B();

    public string CommonText => "common text view model 2";

    private object _gridViewModel;
    public object GridViewModel
    {
        get => _gridViewModel;
        set => _UpdateField(ref _gridViewModel, value);
    }

    public ICommand SetGridToShowCommand { get; }

    public ViewModel2()
    {
        SetGridToShowCommand = new SetGridToShow(this);
    }

    private class SetGridToShow : ICommand
    {
        private readonly ViewModel2 _owner;

        public SetGridToShow(ViewModel2 owner)
        {
            _owner = owner;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter) => true;

        public void Execute(object parameter)
        {
            if (parameter is int index ||
                (parameter is string text && int.TryParse(text, out index)))
            {
                _owner.SetGridToShowIndex(index);
            }
        }
    }

    private void SetGridToShowIndex(int index)
    {
        GridViewModel = index == 1 ? (object)_viewModel2A : _viewModel2B;
    }
}

And the two “unique” ones:

class ViewModel2A
{
    public string UniqueText1 => "unique text grid #1";
}

class ViewModel2B
{
    public string UniqueText2 => "unique text grid #2";
}

I skipped the INotifyPropertyChanged for the purposes of this example, and just made these view models with read-only/display-only properties.

Note that in the main view model, all it does when the user input occurs, is to set the current “grid” view model to the appropriate “unique” view model object.

With that in place, we can write the XAML:

<DockPanel Grid.Column="1">
  <DockPanel.DataContext>
    <l:ViewModel2/>
  </DockPanel.DataContext>
  <DockPanel.Resources>
    <DataTemplate DataType="{x:Type l:ViewModel2A}">
      <StackPanel>
        <!-- OneWay binding for illustration purposes (view model property is read-only) -->
        <!-- RelativeSource allows for referencing properties from other than the current data context, such as the common text property -->
        <TextBox Text="{Binding DataContext.CommonText, Mode=OneWay, RelativeSource={RelativeSource AncestorType=DockPanel}}"/>
        <TextBox Text="{Binding UniqueText1, Mode=OneWay}"/>
      </StackPanel>
    </DataTemplate>
    <DataTemplate DataType="{x:Type l:ViewModel2B}">
      <StackPanel>
        <TextBox Text="{Binding DataContext.CommonText, Mode=OneWay, RelativeSource={RelativeSource AncestorType=DockPanel}}"/>
        <TextBox Text="{Binding UniqueText2, Mode=OneWay}"/>
      </StackPanel>
    </DataTemplate>
  </DockPanel.Resources>
  <ToolBarTray DockPanel.Dock="Top">
    <ToolBar>
      <Button Content="Show Grid 1" Command="{Binding SetGridToShowCommand}" CommandParameter="1"/>
      <Button Content="Show Grid 2" Command="{Binding SetGridToShowCommand}" CommandParameter="2"/>
    </ToolBar>
  </ToolBarTray>
  <Grid>
    <ContentControl Content="{Binding GridViewModel}"/>
  </Grid>
</DockPanel>

Here, rather than setting styles with triggers, there are two different templates declared in the resource dictionary of the parent DockPanel element, one for each “unique” view model type. Then in the Grid control, the content is simply bound to the current “unique” view model object. WPF will select the correct template according to the type of that current “unique” view model object.

One slightly complicated thing I did in the XAML above was to put the CommonText property in the main view model, making it actually common to both view states. Then the templates both refer to it by using the RelativeSource mode for the binding. It would also have been possible instead to have the data templates only provide UI elements for the “unique” properties, and have the parent UI element handle display of the CommonText property. That would arguably be a little cleaner and less repetitive, but it would also have been a significant enough departure from the code you originally posted that I decided not to cross that bridge. 🙂


Finally, all of the above relies on that base class I mentioned earlier to implement INotifyPropertyChanged. There are various ways to implement that, but to complete the above example, here’s the implementation I used for the code above:

class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void _UpdateField<T>(ref T field, T newValue,
        Action<T> onChangedCallback = null,
        [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, newValue))
        {
            return;
        }

        T oldValue = field;

        field = newValue;
        onChangedCallback?.Invoke(oldValue);
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top