简体   繁体   中英

WPF & MVVM Light - Dynamically display UserControls within a UserControl

I am working with WPF and the MVVM Light framework. In my project I have a GameViewModel and an associated view for it that acts as the main screen of my game (this is not my main ViewModel). Within the GameView, I would like to dynamically display other UserControls, triggered by button clicks or something of that nature, at a certain location within the grid. I'm not entirely sure how to go about this. I thought about using a secondary ViewModelLocator or something similar (which I wouldn't be sure how to go about doing at all), but before I dove into that I thought I'd ask here and see if there are any more practical ways to do this.

Say a button is clicked, UserControl1 is displayed at 3, 3 on the grid, then another button is clicked and UserControl2 is displayed at the same location. This is essentially what I'm trying to accomplish. While it is not imperative, I would like to use as much XAML and as little code-behind as possible, but anything that gets the job done in an MVVM-friendly manner will work perfectly fine for me. Any advice that you could give would be greatly appreciated. I'm guessing the solution is probably a lot easier than I'm making it out to be... it usually is.

One idea that I used on a recent project is to bind the Visibility property of the element to a Visibility property in the ViewModel. Then you can handle all of the displaying logic in the ViewModel.

Visibility="{Binding ElementVisibility}"

Then in the ViewModel you would have a property like

public const string ElementVisibilityPropertyname = "ElementVisibility";
private Visibility _elementVisibility = Visibility.Collapsed;
public Visibility ElementVisibility
{
    get
    {
        return _elementVisibility;
    }
    set
    {
        if (_elementVisibility== value)
        {
            return;
        }

        RaisePropertyChanging(ElementVisibilityPropertyname );
        _elementVisibility= value;
        RaisePropertyChanged(ElementVisibilityPropertyname );
    }
}

In your Button you would bind the Command property like so:

Command="{Binding ShowElement}"

And then in the ViewModel you would provide a RelayCommand

private RelayCommand _showElement;
public RelayCommand ShowElement
{
    get
    {
        return _showElement?? (_showElement= new RelayCommand(() =>
                             {
                                 this.ElementVisibility = Visibility.Visible;
                             ));
    }
}

Hopefully this gets you in the direction you are looking for!

I made a blog post about something quite similair here;

http://pjgcreations.blogspot.co.uk/2013/03/wpf-programmatically-adding-buttons.html

The following uses a Canvas, an ItemPresenter and a DataTemplate to show buttons populated from the ViewModel at runtime.

XAML:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Canvas x:Name="MyCanvas">
        <ItemsControl ItemsSource="{Binding MyButtons}" Height="237" Width="507">
            <ItemsControl.ItemsPanel >
                <ItemsPanelTemplate>
                    <Canvas IsItemsHost="true"></Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Margin="{Binding ControlMargin}" Content="{Binding Content}" Command="{Binding DataContext.ButtonCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding ProductId}"></Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Canvas>
</Window>

A Fluid Button Class;

Public Class FluidButton

    Public Property Content As String
    Public Property LeftPos As Double
    Public Property TopPos As Double
    Public Property ProductId As Double

    'Returns the Control Margin, using the Class Properties
    Public ReadOnly Property ControlMargin As Thickness
        Get
            Return New Thickness With {.Left = LeftPos, .Top = TopPos}
        End Get
    End Property

End Class

Properties in Your ViewModel;

'Our Collection of Buttons or Products
Public Property MyButtons As ObservableCollection(Of FluidButton)

'Used to expose the Button Pressed Execute Commands to the UI for Binding
Public Property ButtonCommand As DelegateCommand

To Add some Buttons;

MyButtons = New ObservableCollection(Of FluidButton)

MyButtons.Add(New FluidButton With {.Content = "Test1", .LeftPos = 0, .TopPos = 20, .ProductId = 1})
MyButtons.Add(New FluidButton With {.Content = "Test2", .LeftPos = 40, .TopPos = 30, .ProductId = 2})
MyButtons.Add(New FluidButton With {.Content = "Test3", .LeftPos = 80, .TopPos = 40, .ProductId = 3})
MyButtons.Add(New FluidButton With {.Content = "Test4", .LeftPos = 120, .TopPos = 50, .ProductId = 4})
MyButtons.Add(New FluidButton With {.Content = "Test5", .LeftPos = 160, .TopPos = 60, .ProductId = 5})

I'm sure you could easily modify this to suit your needs

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM