简体   繁体   English

WPF datagrid:添加行时查找控件

[英]WPF datagrid: Finding a control when adding a row

So, basically I have a list of jobs that I'm keeping track of in a Datagrid. 因此,基本上,我有一个要在Datagrid中跟踪的作业列表。 In that datagrid, I have a button I want to be a "Cancel" button when the job is running, but otherwise be a "Retry" button. 在该数据网格中,我有一个按钮,我想在作业运行时成为“取消”按钮,否则我想成为“重试”按钮。

So, I've added the button to my grid: 因此,我将按钮添加到了网格中:

<DataGridTemplateColumn x:Name="JobActionColumn" Header="">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Grid>
                <Button Click="JobActionButton_Click" Content="Resend" Name="JobActionButton" Height="18" Width="45" Margin="0,0,0,0" />
            </Grid> 
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

And in the code, I add my object in an ObservableCollection to add it to the grid: 在代码中,我将对象添加到ObservableCollection中以将其添加到网格中:

    _jobs.Add(job);
    CollectionViewSource jobViewSource = this.FindViewSource("JobViewSource");
    jobViewSource.View.Refresh(); // Ensure that the new job appears at the top of the grid.
    JobDataGrid.SelectedItem = job;

    // Note: The Controller.Completed event handler disposes the controller object.
    Controller controller = new Controller(_historyContext);
    _controllers.Add(controller);
    controller.Completed += Controller_Completed;
    controller.Process(job);

    GetGridButton("JobActionButton", job).Content = "Cancel";

With GetGridButton being: 与GetGridButton是:

    private Button GetGridButton(string name, Job job)
    {            
        var selectedRow = (DataGridRow)JobDataGrid.ItemContainerGenerator.ContainerFromItem(job);

        return ExtensionMethods.FindVisualChildren<Button>(selectedRow).First(x => x.Name == name);            
    }

I've confirmed that GetGridButton works with rows that already exist. 我已经确认GetGridButton可以处理已经存在的行。 The problem is that when you add a new row to the underlying dataset and call this, it can't find the DataGridRow . 问题是,当您向基础数据集添加新行并调用它时,它找不到DataGridRow I assume this is because it hasn't been created yet. 我认为这是因为尚未创建。 So, looking through events, it looked like that the LoadingRow event would be a good candidate: 因此,浏览事件,看起来LoadingRow事件将是一个不错的选择:

    private void JobDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
    {
        Job job = (Job)e.Row.Item;

        if (_controllers.FirstOrDefault(x => x.Job == job) != null)
        {
            var y = ExtensionMethods.FindVisualChildren<Button>(e.Row);
            Button button = ExtensionMethods.FindVisualChildren<Button>(e.Row).First(x => x.Name == "JobActionButton");
            button.Content = "Cancel";
        }
    }

So, now there is a DataGridRow object to pass into FindVisualChildren , but it still doesn't find any buttons. 因此,现在有一个DataGridRow对象可以传递给FindVisualChildren ,但是它仍然找不到任何按钮。 So, is there any way for me to access this button on an added row? 那么,有什么办法让我访问添加行上的此按钮?

The preferred way to work with WPF is called MVVM . 使用WPF的首选方法称为MVVM

This is my take on what you described: 这是我对您所描述的看法:

<Window x:Class="MiscSamples.MVVMDataGrid"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MVVMDataGrid" Height="300" Width="300">
    <DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False"
              CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Name}"/>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Button Command="{Binding CancelCommand}" Content="Resend" 
                                    Height="20" Width="45" Margin="0,0,0,0" x:Name="btn" />
                        </Grid>

                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding IsRunning}" Value="True">
                                <Setter TargetName="btn" Property="Content" Value="Cancel"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>

                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Window>

Code Behind: 背后的代码:

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

        DataContext = Enumerable.Range(1, 5)
                                .Select(x => new Job {Name = "Job" + x})
                                .ToList();
    }
}

Data Item: 数据项:

public class Job: PropertyChangedBase
{
    public string Name { get; set; }

    private bool _isRunning;
    public bool IsRunning
    {
        get { return _isRunning; }
        set
        {
            _isRunning = value;
            OnPropertyChanged("IsRunning");
        }
    }

    public Command CancelCommand { get; set; }

    public Job()
    {
        CancelCommand = new Command(() => IsRunning = !IsRunning);
    }
}

PropertyChangedBase class (MVVM helper class): PropertyChangedBase类(MVVM帮助器类):

public class PropertyChangedBase:INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Command class (MVVM Helper class): 命令类(MVVM Helper类):

 //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;
        }
    }

Result: 结果:

在此处输入图片说明

  • Notice how I'm leveraging DataBinding in order to simplify the code and remove the need to find the element in the Visual Tree, and manipulating it in procedural code. 请注意,我是如何利用DataBinding来简化代码并消除在可视树中查找元素的需要,并在过程代码中进行操作。
  • The logic is completely decoupled from the View, by the use of the Command that abstracts the button functionality. 通过使用抽象按钮功能的命令 ,逻辑与视图完全分离。
  • Not a single line of code in the Code Behind. 后面的代码中没有一行代码。 Only boilerplate generating the sample entries. 仅样板生成样本条目。
  • Copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself. 将我的代码复制并粘贴到File -> New Project -> WPF Application然后亲自查看结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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