Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
332 views
in Technique[技术] by (71.8m points)

c# - How to dynamically add controls to a WrapPanel in another class?

I'm migrating from VB to C# and decided that WPF would be the best option for me as the programs I've been developing are highly GUI dependant based applications. However, C# is causing me a lot of headaches, confusion, and frustration when trying to get what are simple tasks in VB to work in C# code. In VB, I can get this working VERY easily. But in C#, after spending countless hours (now days) searching and playing with code, I'm still non the wiser how to get this working.

My scenario:

  • I have 2 xaml pages.
  • The first xaml page has a wrappanel.
  • The second xaml page has a button that will create a new button and add it to the wrappanel in xaml page 1.

I can easily add a new button to the wrappanel when using the code below in page1.xaml.cs:

Button New_Button = new Button();
My_WrapPanel.Children.Add(New_Button);

I've also tried calling a method located in page1 from page2 to create the button but the new button does not show in the wrappanel !?

I would really appreciate some help and possibly a simple code example to help me on my way.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok, Im using UserControls instead of Pages to keep them in a single Window. Since you didn't post any XAML, I have no idea what your real need is, but here is my take:

MultiPageSample.xaml ("Main Window"):

<Window x:Class="MiscSamples.MultiPageMVVM.MultiPageSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MiscSamples.MultiPageMVVM"
        Title="MultiPageSample" Height="300" Width="300">
    <UniformGrid Rows="1" Columns="2">
        <local:Page1 DataContext="{Binding Page1}"/>
        <local:Page2 DataContext="{Binding Page2}"/>
    </UniformGrid>
</Window>

Code Behind:

public partial class MultiPageSample : Window
{
    public MultiPageSample()
    {
        InitializeComponent();

        DataContext = new MultiPageViewModel();
    }
}

ViewModel:

public class MultiPageViewModel
{
    public Page1ViewModel Page1 { get; set; }

    public Page2ViewModel Page2 { get; set; }

    public MultiPageViewModel()
    {
        Page1 = new Page1ViewModel();
        Page2 = new Page2ViewModel();

        Page2.AddNewCommand = new Command(Page1.AddCommand);
    }
}

Page1:

<UserControl x:Class="MiscSamples.MultiPageMVVM.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ItemsControl ItemsSource="{Binding Commands}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Command="{Binding}" Content="Click Me!"
                        Margin="2"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</UserControl>

Code Behind:

public partial class Page1 : UserControl
{
    public Page1()
    {
        InitializeComponent();
    }
}

Page2:

<UserControl x:Class="MiscSamples.MultiPageMVVM.Page2"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Button Content="Add New Command (I Mean Button)"
            VerticalAlignment="Center" HorizontalAlignment="Center"
            Command="{Binding AddNewCommand}"/>
</UserControl>

Code Behind:

public partial class Page2 : UserControl
{
    public Page2()
    {
        InitializeComponent();
    }
}

ViewModel:

public class Page2ViewModel
{
    public Command AddNewCommand { get; set; }
}

Command class (can be found on most MVVM frameworks)

//Dead-simple implementation of ICommand
    //Serves as an abstraction of Actions performed by the user via interaction with the UI (for instance, Button Click)
    public class Command : ICommand
    {
        public Action Action { get; set; }

        public void Execute(object parameter)
        {
            if (Action != null)
                Action();
        }

        public bool CanExecute(object parameter)
        {
            return IsEnabled;
        }

        private bool _isEnabled = true;
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                _isEnabled = value;
                if (CanExecuteChanged != null)
                    CanExecuteChanged(this, EventArgs.Empty);
            }
        }

        public event EventHandler CanExecuteChanged;

        public Command(Action action)
        {
            Action = action;
        }
    }

    public class Command<T>: ICommand
    {
        public Action<T> Action { get; set; }

        public void Execute(object parameter)
        {
            if (Action != null && parameter is T)
                Action((T)parameter);
        }

        public bool CanExecute(object parameter)
        {
            return IsEnabled;
        }

        private bool _isEnabled;
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                _isEnabled = value;
                if (CanExecuteChanged != null)
                    CanExecuteChanged(this, EventArgs.Empty);
            }
        }

        public event EventHandler CanExecuteChanged;

        public Command(Action<T> action)
        {
            Action = action;
        }
    }

Result:

enter image description here

Now, the explanation of all this mess:

First of all, you must leave behind the traditional mentality of Manipulating UI elements in code, and embrace MVVM.

WPF has very powerful DataBinding capabilities that are utterly absent in ancient dinosaur frameworks.

Notice how I'm using the reusable Command class (which is kind of a basic part of most MVVM frameworks) to represent the Buttons in the Page1ViewModel. These instances of Command are then added to the ObservableCollection, which in turn notifies WPF when an element is added or removed to it, and thus the UI is automatically updated by the Binding.

Then, the DataTemplate defined as the ItemTemplate for the ItemsControl in Page1 is used to "render" each item inside the ObservableCollection.

This is what I refer to when I say WPF needs a really different mindset to work with. This is the default approach to EVERYTHING in WPF. You almost NEVER have the need to reference / create / manipulate UI elements in procedural code. That's what XAML is for.

Also notice that this could be simplified A LOT by using the same ViewModel for both Pages, but I kept them separate on purpose just to show you this case where you have different ViewModels communicating with each other directly.

Let me know if you have any doubts.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...