0

所以,基本上我有一个我在 Datagrid 中跟踪的作业列表。在那个数据网格中,我有一个按钮,我想在作业运行时成为“取消”按钮,否则是“重试”按钮。

所以,我已将按钮添加到我的网格中:

<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>

在代码中,我将我的对象添加到 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";

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

我已经确认GetGridButton适用于已经存在的行。问题是,当您向基础数据集添加新行并调用它时,它找不到DataGridRow。我认为这是因为它尚未创建。因此,查看事件,看起来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";
        }
    }

所以,现在有一个DataGridRow对象可以传递给FindVisualChildren,但它仍然没有找到任何按钮。那么,有什么方法可以让我在添加的行上访问此按钮?

4

1 回答 1

0

使用 WPF 的首选方法称为MVVM

这是我对您描述的内容的看法:

<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>

代码背后:

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

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

数据项:

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 类(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));
    }
}

命令类(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;
        }
    }

结果:

在此处输入图像描述

  • 请注意我如何利用DataBinding来简化代码并消除在可视树中查找元素并在过程代码中对其进行操作的需要。
  • 通过使用抽象按钮功能的命令,逻辑与视图完全分离。
  • 代码背后没有一行代码。仅生成样本条目的样板。
  • 将我的代码复制并粘贴到 a 中File -> New Project -> WPF Application,然后自己查看结果。
于 2013-08-22T17:44:40.827 回答