WPF:删除Grid中的行会使其他行重叠

时间:2017-11-22 18:45:27

标签: c# wpf

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

初始状态

第0行被删除后

After row 0 has been deleted

要复制问题,请尝试使用以下步骤的小型wpf应用程序:

  1. 将代码粘贴到新的WPF应用程序中并运行它。
  2. 多次点击“添加新行”按钮(> 2)。
  3. 单击其中一个删除按钮(除了最后一个)。
  4. 你应该看到问题。
  5. 的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社区

1 个答案:

答案 0 :(得分: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>

“Viewmodels”:从技术上讲,它们不是真正的视图模型,因为它们不需要引发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();
    }