【问题标题】:WPF: Removing a row in Grid makes other rows overlappingWPF:删除网格中的一行会使其他行重叠
【发布时间】:2017-11-22 18:45:26
【问题描述】:

我需要以编程方式将带有多个控件的新行添加到网格中,并且这些行和控件也需要在一段时间后被删除。问题是,每当删除一行(不是最后一行)时,几行可能会重叠。

初始状态

第 0 行被删除后

要重现问题,请按照以下步骤尝试以下小型 wpf 应用程序:

  1. 将代码粘贴到新的 WPF 应用程序中并运行它。
  2. 多次单击“添加新行”按钮 (>2)。
  3. 单击其中一个删除按钮(最后一个除外)。
  4. 您应该会看到问题所在。

xml:

<Window x:Class="WpfApplication1.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:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="MainGrid">

    </Grid>
</Window>

C#代码:

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {

        // secondary grid
        Grid _sGrid = new Grid();

        public MainWindow()
        {
            InitializeComponent();
            SizeToContent = SizeToContent.Height;
            // divid the initial grid into two rows.
            MainGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            MainGrid.RowDefinitions.Add(new RowDefinition());

            // add a stack panel to the top row.
            var sp = new StackPanel();
            Grid.SetRow(sp, 0);
            // add a "add a new row" button to the stack panel.
            var btn = new Button { Content = "add a new row" };
            btn.Click += AddRow;
            sp.Children.Add(btn);

            // add a secondary grid to the bottom row.
            Grid.SetRow(_sGrid, 1);

            // add controls
            MainGrid.Children.Add(sp);
            MainGrid.Children.Add(_sGrid);
        }

        private int _rowCount;
        private int _rowSerialNo;
        public void AddRow(object sender, RoutedEventArgs e)
        {
            // add a row in the secondary grid, set height to auto
            var rd = new RowDefinition {Height = GridLength.Auto};
            _sGrid.RowDefinitions.Add(rd);

            // add a label to the row
            var lbl = new Label {Content = $"This is row {_rowSerialNo}"};
            Grid.SetRow(lbl,_rowCount);
            _sGrid.Children.Add(lbl);

            // add a button to the top row of the main grid for deleting this new row
            var btn = new Button {Content = $"del row {_rowSerialNo}"};
            btn.Click += DelRow;
            (MainGrid.Children[0] as StackPanel).Children.Add(btn);
            // set resources to make it easier to clear all contents in the row
            btn.Resources.Add("rd", rd);
            btn.Resources.Add("lbl",lbl);

            // advance row count
            _rowCount++;
            _rowSerialNo++;
        }

        private void DelRow(object sender, RoutedEventArgs e)
        {
            // remove contents
            _sGrid.Children.Remove((UIElement)(sender as Button).Resources["lbl"]);

            // remove row definition
            _sGrid.RowDefinitions.Remove((RowDefinition)(sender as Button).Resources["rd"]);

            // remove the delete button
            (MainGrid.Children[0] as StackPanel).Children.Remove((UIElement)sender);

            _rowCount--;
        }
    }
}

我正在使用 VS2015 社区

【问题讨论】:

  • 您可以使用 ItemsControl 为自己省去很多麻烦,但这是您的生命。
  • 不知道 ItemsControl。但我想我现在可能会找出问题所在,哈哈。它可能是 Grid.Set。我需要将下面行中的控件重置为 -1 行..
  • 看起来这里的问题可能是您正在删除行定义,但没有删除与它们关联的控件。
  • @EdPlunkett 我确实删除了控件。在演示中,它是标签。

标签: c# wpf


【解决方案1】:

这是一个快速的 MVVM 实现,它在我看来就像你在做的那样。这就是我使用 ItemsControl 的意思。代码行数更多,但几乎都是声明性样板文件,因此很难出错。

这种方法的价值在于您可以在集合中添加和删除项目,并且视图会自动更新以反映集合的当前内容。添加项目是一个简单的步骤:添加项目。删除它是一个简单的步骤:删除该项目。如果您更改项目的显示方式,则可以通过更改 XAML 来完成;它与如何添加或删除项目无关。

如果您对它的工作原理有任何疑问,请提出。

XAML:

<StackPanel>
    <Button Command="{Binding AddRowItemCommand}">Add Row</Button>
    <ItemsControl
        ItemsSource="{Binding RowItems}"
        >
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button 
                    Content="{Binding Text}"    
                    ContentStringFormat="{}Delete {0}"
                    Command="{Binding DataContext.RemoveRowItemCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" 
                    CommandParameter="{Binding}"
                    />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <ItemsControl
        ItemsSource="{Binding RowItems}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding Text}" ContentStringFormat="{}This is {0}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

</StackPanel>

“视图模型”:从技术上讲,这些并不是真正的视图模型,因为它们没有任何需要提出INotifyPropertyChanged.PropertyChanged

public class MainViewModel
{
    public MainViewModel()
    {
        RemoveRowItemCommand = new DelegateCommand<RowItem>(RemoveRowItem);
        AddRowItemCommand = new DelegateCommand<object>(AddRowItem);
    }

    public ObservableCollection<RowItem> RowItems { get; } = new ObservableCollection<RowItem>();

    public ICommand RemoveRowItemCommand { get; private set; }
    public ICommand AddRowItemCommand { get; private set; }

    public void RemoveRowItem(RowItem rowItem)
    {
        RowItems.Remove(rowItem);
    }

    private int _nextRowItemID = 0;
    public void AddRowItem(object unused)
    {
        RowItems.Add(new RowItem { Text = $"Row {++_nextRowItemID}" });
    }
}

public class DelegateCommand<TParam> : ICommand
{
    public DelegateCommand(Action<TParam> exec)
    {
        _execute = exec;
    }

    public event EventHandler CanExecuteChanged;

    private Action<TParam> _execute;
    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _execute?.Invoke(parameter == null ? default(TParam) : (TParam)parameter);
    }
}

public class RowItem
{
    public String Text { get; set; }
}

后面的代码:

    public MainWindow()
    {
        InitializeComponent();

        DataContext = new MainViewModel();
    }

【讨论】:

    猜你喜欢
    • 2013-08-31
    • 1970-01-01
    • 2017-02-07
    • 1970-01-01
    • 2012-12-11
    • 2019-05-29
    • 2021-11-14
    • 2023-03-21
    • 1970-01-01
    相关资源
    最近更新 更多