C# 如何以编程方式在 Wpf 中的网格行内创建网格

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19080861/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 14:02:00  来源:igfitidea点击:

How to Programmatically create Grid within row of a Grid in Wpf

c#.netwpf

提问by Tameen Malik

I am working in WPF -- There is buttonwith click event handlerin my application. As i click on button it's event handler generates a new row in grid named as grids. In this new Row i want to add another grid programmatically to add Label, Button and TextBox in this grid in row. As i executed my code it only generates a texboxes! where labels and button shown once! Here code and image is : Please feel free to ask if my query is not clear to you!

我在 WPF 工作——在我的应用程序中有buttonwith click event handler。当我点击按钮时,它的事件处理程序在网格中生成一个名为grids. 在这个新行中,我想以编程方式添加另一个网格,以便在此网格中按行添加标签、按钮和文本框。当我执行我的代码时,它只生成一个文本框!标签和按钮显示一次!这里的代码和图片是:如果我的查询对您来说不清楚,请随时询问!

 int r =0;
 private void button2_Click(object sender, RoutedEventArgs e)
    {
        TextEdit text1; Button button1; Grid grid1;
        grids.RowDefinitions.Add(new RowDefinition());
        text1 = new TextEdit();
        text1.SetValue(Grid.ColumnProperty, 1);
        text1.SetValue(Grid.RowProperty, r);
        button1 = new Button();
        button1.Content = "Left + " + r;
        button1.Click += new RoutedEventHandler(button1_Click);
        button1.SetValue(Grid.ColumnProperty, 1);
        button1.SetValue(Grid.RowProperty, r);
        grid1 = new Grid();
        grid1.SetValue(Grid.ColumnProperty, 1);
        grids.RowDefinitions.Add(new RowDefinition());
        grid1.SetValue(Grid.RowProperty, r);
        grids.Children.Add(button1);
        grids.Children.Add(text1);
        r = r + 1;
    }

enter image description hereEDIT

在此处输入图片说明编辑

 int r =0;
 private void button2_Click(object sender, RoutedEventArgs e)
  {
    db obj = new db();
    var query = from p in obj.TableA select p ;

  foreach(var a in query.ToList())
  {
    TextEdit text1; Button button1; Grid grid1;
    grids.RowDefinitions.Add(new RowDefinition());
    text1 = new TextEdit();
    text1.SetValue(Grid.ColumnProperty, 1);
    text1.SetValue(Grid.RowProperty, r);
    button1 = new Button();
    button1.Content = a.name;
    button1.Click += new RoutedEventHandler(button1_Click);
    button1.SetValue(Grid.ColumnProperty, 1);
    button1.SetValue(Grid.RowProperty, r);
    grid1 = new Grid();
    grid1.SetValue(Grid.ColumnProperty, 1);
    grids.RowDefinitions.Add(new RowDefinition());
    grid1.SetValue(Grid.RowProperty, r);
    grids.Children.Add(button1);
    grids.Children.Add(text1);
    r = r + 1;}
}

采纳答案by Federico Berasategui

Ok. Delete all your code and start all over.

好的。删除所有代码并重新开始。

If you're working with WPF, you really need to have The WPF Mentality

如果你正在使用 WPF,你真的需要拥有WPF 心态

As a general rule, you almost nevercreate or manipulate UI elements in procedural code in WPF. That's what XAML is for.

作为一般规则,您几乎从不在 WPF 的过程代码中创建或操作 UI 元素。这就是 XAML 的用途。

This the right way to do what you're asking in WPF (in a full working example):

这是在 WPF 中执行您要求的正确方法(在一个完整的工作示例中):

<Window x:Class="MiscSamples.ItemsControlSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
        Title="ItemsControlSample" Height="300" Width="300">
    <DockPanel>
        <Button Content="Add New Row" Command="{Binding AddNewRowCommand}"
                DockPanel.Dock="Bottom"/>

        <ItemsControl ItemsSource="{Binding Data}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border  BorderBrush="Black" Background="Gainsboro" BorderThickness="1" Margin="2">
                        <!-- This is the Inner Grid for each element, which is represented in Brown color in your picture -->
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition Width=".2*"/>
                                <ColumnDefinition Width=".2*"/>
                            </Grid.ColumnDefinitions>

                            <Label Content="{Binding Label1Text}"
                                   Margin="2"/>

                            <Button Content="Button1" 
                                    Command="{Binding DataContext.Command1, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                    CommandParameter="{Binding}"
                                    Grid.Column="1" Margin="2"/>

                            <Button Content="Button2" 
                                    Command="{Binding DataContext.Command2, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                    CommandParameter="{Binding}"
                                    Grid.Column="2" Margin="2"/>

                            <dxe:TextEdit Text="{Binding Text}"
                                          Grid.Row="1" Grid.ColumnSpan="3"
                                          Margin="2"/>
                        </Grid>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>

            <ItemsControl.Template>
                <ControlTemplate TargetType="ItemsControl">
                    <ScrollViewer CanContentScroll="True">
                        <ItemsPresenter/>
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </DockPanel>
</Window>

Code Behind:

背后的代码:

public partial class ItemsControlSample : Window
{
    public ItemsControlSample()
    {
        InitializeComponent();
        DataContext = new ItemsControlSampleViewModel();
    }
}

ViewModel:

视图模型:

public class ItemsControlSampleViewModel
{
    public ObservableCollection<ItemsControlSampleData> Data { get; set; }

    public Command AddNewRowCommand { get; set; }

    public Command<ItemsControlSampleData> Command1 { get; set; }

    public Command<ItemsControlSampleData> Command2 { get; set; }

    public ItemsControlSampleViewModel()
    {
        var sampledata = Enumerable.Range(0, 10)
                                   .Select(x => new ItemsControlSampleData()
                                                {
                                                    Label1Text = "Label1 " + x.ToString(),
                                                    Text = "Text" + x.ToString()
                                                });

        Data = new ObservableCollection<ItemsControlSampleData>(sampledata);
        AddNewRowCommand = new Command(AddNewRow);
        Command1 = new Command<ItemsControlSampleData>(ExecuteCommand1);
        Command2 = new Command<ItemsControlSampleData>(ExecuteCommand2);

    }

    private void AddNewRow()
    {
        Data.Add(new ItemsControlSampleData() {Label1Text = "Label 1 - New Row", Text = "New Row Text"});
    }

    private void ExecuteCommand1(ItemsControlSampleData data)
    {
        MessageBox.Show("Command1 - " + data.Label1Text);
    }

    private void ExecuteCommand2(ItemsControlSampleData data)
    {
        MessageBox.Show("Command2 - " + data.Label1Text);
    }
}

Data Item:

数据项:

public class ItemsControlSampleData
{
    public string Label1Text { get; set; }

    public string Text { get; set; }
}

Helper classes:

辅助类:

public class Command : ICommand
{
    public Action Action { get; set; }

    public string DisplayName { 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 = true;
    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

在此处输入图片说明

  • Notice how I'm not dealing with UI in procedural code, but instead I'm using DataBindingand simple, simple properties. That's how you program in WPF. That's what the WPF mentality is about.
  • I'm using an ItemsControland a DataTemplatedefined in XAML to let WPF create the UI for each of my data items.
  • Also notice how my code does nothing except expose the data and define reusable Commandsthat serve as abstractions to the user actions such as Button clicks. This way you can concentrate in coding your business logic instead of struggling with how to make the UI work.
  • The buttons inside each item are bound to the Commands using a RelativeSourceBinding to navigate upwards in the Visual Tree and find the DataContext of the ItemsControl, where the Commands are actually defined.
  • When you need to add a new item, you just add a new item to the ObservableCollectionthat contains your data and WPF automatically creates the new UI elements bound to that.
  • Though this might seem like "too much code", most of the code I posted here is highly reusable and can be implemented in a Generic ViewModel<T>that is then reusable for any type of data items. Commandand Command<T>are also write-once reusable classes that can be found in any MVVM framework such as Prism, MVVM Lightor Caliburn.Micro.
  • This approach is really much preferred in WPF, because it enables a great amount of scalability and independence between the UI and the business logic, and it also enables testability of the ViewModel.
  • I suggest you read all the materials linked in the post, most importantly Rachel's WPF Mentality and related blog posts. Let me know if you need further help.
  • WPF Rocks. Just copy and paste my code in a File -> New Project -> WPF Applicationand see the results for yourself.
  • 请注意,我不是在程序代码中处理 UI,而是使用DataBinding简单、简单的属性。这就是您在 WPF 中编程的方式。这就是 WPF 的心态。
  • 我正在使用XAML 中定义的ItemsControlDataTemplate来让 WPF 为我的每个数据项创建 UI。
  • 还要注意我的代码除了公开数据和定义可重用的命令之外什么都不做,这些命令用作用户操作(例如按钮单击)的抽象。通过这种方式,您可以专注于编写业务逻辑,而不是为如何使 UI 工作而苦苦挣扎。
  • 每个项目内的按钮都使用RelativeSourceBinding 绑定到Commands以在Visual Tree 中向上导航并找到ItemsControl 的DataContext,其中实际定义了Commands。
  • 当您需要添加一个新项目时,您只需向ObservableCollection包含您的数据的 中添加一个新项目,WPF 就会自动创建绑定到该项目的新 UI 元素。
  • 虽然这看起来像是“代码太多”,但我在这里发布的大部分代码都是高度可重用的,并且可以在一个泛型ViewModel<T>中实现,然后可重用于任何类型的数据项。Command并且Command<T>也是一次性编写的可重用类,可以在任何 MVVM 框架中找到,例如PrismMVVM LightCaliburn.Micro
  • 这种方法在 WPF 中非常受欢迎,因为它在 UI 和业务逻辑之间实现了大量的可扩展性和独立性,并且还支持 ViewModel 的可测试性。
  • 我建议您阅读文章中链接的所有材料,最重要的是 Rachel 的 WPF Mentality 和相关博客文章。如果您需要进一步的帮助,请告诉我。
  • WPF 摇滚。只需将我的代码复制并粘贴到 a 中,File -> New Project -> WPF Application然后自己查看结果。

回答by ArchAngel

It's actually much easier in behind code then in xaml code..

实际上,在后面的代码中比在 xaml 代码中容易得多。

My Xaml code:

我的 Xaml 代码:

<Window x:Class="WpfAddGridWithStackPanel.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">
        <Grid>
        <Grid x:Name="Grid_Grid" Margin="0,0,0,32">

        <Grid>
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <Grid x:Name="Grid_Grid" Margin="0,0,0,32"/>
    </ScrollViewer>
    <Button x:Name="btn_Add" Height="32" DockPanel.Dock="Bottom" VerticalAlignment="Bottom" Content="Add New Row" Click="btn_Add_Click" Width="150" HorizontalAlignment="Left" UseLayoutRounding="True" />
    <Button x:Name="btn_Remove" Height="32" DockPanel.Dock="Bottom" VerticalAlignment="Bottom" Content="Remove last Row" Click="btn_Remove_Click" Width="150" HorizontalAlignment="Right" />
</Grid>
</Window>

And Code behind:

和后面的代码:

public partial class MainWindow : Window
    {
        int num = 0;
        public MainWindow()
        {
            InitializeComponent();
        }

        void btn1_Click(object sender, RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

        void btn2_Click(object sender, RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

        private void btn_Remove_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Grid_Grid.RowDefinitions.RemoveAt(Grid_Grid.RowDefinitions.Count - 1);
                Grid_Grid.Children.RemoveAt(Grid_Grid.Children.Count - 1);
                num--;
            }
            catch { }
        }

        private void btn_Add_Click(object sender, RoutedEventArgs e)
        {
            StackPanel stack = new StackPanel();
            DockPanel dock = new DockPanel();
            Label lbl = new Label();
            Button btn1 = new Button();
            Button btn2 = new Button();
            TextBox txt1 = new TextBox();

            stack.Children.Add(dock);
            stack.Children.Add(txt1);
            dock.Children.Add(lbl);
            dock.Children.Add(btn2);
            dock.Children.Add(btn1);

            #region StackPanel Properties
            stack.Background = Brushes.LightGray;
            #endregion

            #region DockPanel Content Properties
            lbl.Content = "Label " + (num + 1).ToString();
            lbl.Height = 32;
            lbl.Width = 100;
            lbl.FontSize = 12;
            lbl.SetValue(DockPanel.DockProperty, Dock.Left);
            lbl.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            btn1.Content = "Butten 1";
            btn1.Height = 32;
            btn1.Width = 100;
            btn1.FontSize = 12;
            btn1.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
            btn1.SetValue(DockPanel.DockProperty, Dock.Right);
            btn1.Click += new RoutedEventHandler(btn1_Click);

            btn2.Content = "Butten 2";
            btn2.Height = 32;
            btn2.Width = 100;
            btn2.FontSize = 12;
            btn2.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
            btn2.SetValue(DockPanel.DockProperty, Dock.Right);
            btn2.Click += new RoutedEventHandler(btn2_Click);
            #endregion

            #region TextBox Properties
            txt1.Text = "Text " + (num + 1).ToString();
            txt1.Height = 32;
            txt1.Width = double.NaN;
            txt1.FontSize = 12;
            txt1.Padding = new Thickness(0, 7, 0, 7);
            #endregion

            Grid_Grid.RowDefinitions.Add(new RowDefinition());
            Grid_Grid.RowDefinitions[num].Height = new GridLength(66, GridUnitType.Pixel);
            Grid_Grid.Children.Add(stack);
            stack.SetValue(Grid.RowProperty, num);
            num++;
        }
    }