【问题标题】:Best practice for binding properties of controls within a DataTemplate?在 DataTemplate 中绑定控件属性的最佳实践?
【发布时间】:2016-04-21 20:17:25
【问题描述】:

我看到多年来有很多问题被问到,关于如何最好地与数据模板中的数据绑定,是否有最佳实践?在这种情况下,我希望我的 MemoryCopyBtn 复制当前选定的 MemoryListItem 文本框的文本,以便我可以在 ViewModel 中对其进行处理。

我可以使用 Findname 找到 ListView,但它会在 PageLoad 上显示 ListViewItem 的 null。

我可以将所有内容都放在内存模型页面中,但我认为这不是最佳做法。

我看到这是遍历可视化代码树的各种选项,但我想在运行时执行此操作,这真的是唯一的方法吗?

我有哪些选择? 谢谢。

<ListView x:Name="ClipboardList"
                      xmlns:m="using:QuickieEdit.Models"
                      ItemsSource="{x:Bind ViewModel.MemoryItems}">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="m:MemoryItem">
                            <StackPanel Orientation="Horizontal">
                                <Button x:Name="MemoryCopyBtn"
                                             Content="Copy"
                                             Click="How to Copy currently selected
                                             MemoryListItem.Text?"/> 
                                <TextBox x:Name="MemoryListItem"
                                               Text="{x:Bind Memory, Mode=TwoWay}">
                               </TextBox>                                  
                           </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
</ListView>

【问题讨论】:

  • 单击按钮时,您要复制文本框中的文本,该文本位于同一 DataTemplate 中?
  • 是的。我想复制当前选中或聚焦的 ListViewItem 的 TextBox.Content。

标签: wpf win-universal-app uwp-xaml


【解决方案1】:

如果我对您的理解正确-您要复制与单击的按钮一起位于的文本框的文本,而不是某些选定\聚焦的文本框(否则这没有意义,因为您在其中的每个项目都有一个复制按钮您的列表视图)。如果是这样,只需使用命令:

    <ListView.ItemTemplate>
        <DataTemplate x:DataType="m:MemoryItem">
            <StackPanel Orientation="Horizontal">
                <Button x:Name="MemoryCopyBtn"
                        Content="Copy"
                        Command="{Binding CopyCommand}"
                        CommandParameter="{Binding ElementName=MemoryListItem, Path=Text}" />
                <TextBox x:Name="MemoryListItem"
                         Text="{x:Bind Memory, Mode=TwoWay}"></TextBox>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>

在你的 MemoryItem 中:

public class MemoryItem {
    public MemoryItem() {
        CopyCommand = new RelayCommand(Copy);
    }        

    public string Memory { get; set; }
    public ICommand CopyCommand { get; private set; }

    private void Copy(object argument)
    {
        var text = (string)argument;
        Console.WriteLine(text);
    }
}

【讨论】:

  • 这意味着您正在为列表中的每个项目创建RelayCommand 的实例。在可扩展性方面没有最佳解决方案。
  • @DHN 每个项目的 ICommand 实例对可伸缩性和性能的影响绝对为零。
  • 有趣,每个实例都在消耗内存,不是吗?
  • 当然可以,但是数量可以忽略不计,以至于在 WPF 应用程序中甚至不值得一提。如果你关心这些事情——你不应该首先使用绑定——想象一下每个绑定创建了多少个实例!最好不要使用 WPF - 像 QT 这样的东西会消耗更少的内存。
  • 你是对的。但是,如果我最终做“这里和这里草率”的事情,我的应用程序可能会表现不佳,然后我需要投入大量时间来识别问题并解决它们。所以如果我看到这样的事情,很容易避免,我为什么不这样做呢?
【解决方案2】:

我觉得你可以在你的MemoryItem绑定Button的Click事件,那么你就可以在这个事件中获取被点击按钮的数据上下文了。

只是为了测试,我在这里写了一个很简单的demo:

<ListView x:Name="ClipboardList" ItemsSource="{x:Bind ViewModel.MemoryItems}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="local:MemoryItem">
            <StackPanel Orientation="Horizontal">
                <Button x:Name="MemoryCopyBtn"
                                     Content="Copy"
                                     Click="{x:Bind MemoryCopyBtn_Click}" />
                <TextBox x:Name="MemoryListItem"
                                       Text="{x:Bind Memory, Mode=TwoWay}">
                </TextBox>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

后面的代码:

private MainPageViewModel ViewModel;

public MainPage()
{
    this.InitializeComponent();
    ViewModel = new MainPageViewModel();
}

我的 MainPageViewModel 类:

public class MainPageViewModel
{
    public ObservableCollection<MemoryItem> MemoryItems;

    public MainPageViewModel()
    {
        MemoryItems = new ObservableCollection<MemoryItem>();
        MemoryItems.Clear();
        MemoryItems.Add(new MemoryItem { Memory = "Item 1" });
        MemoryItems.Add(new MemoryItem { Memory = "Item 2" });
        MemoryItems.Add(new MemoryItem { Memory = "Item 3" });
        MemoryItems.Add(new MemoryItem { Memory = "Item 4" });
        MemoryItems.Add(new MemoryItem { Memory = "Item 5" });
        MemoryItems.Add(new MemoryItem { Memory = "Item 6" });
        MemoryItems.Add(new MemoryItem { Memory = "Item 7" });
        MemoryItems.Add(new MemoryItem { Memory = "Item 8" });
    }
}

MemoryItem 类:

public class MemoryItem
{
    public string Memory { get; set; }

    public void MemoryCopyBtn_Click(object sender, RoutedEventArgs e)
    {
        var item = ((FrameworkElement)e.OriginalSource).DataContext as MemoryItem;
        var text = item.Memory;
    }
}

这里的代码和你的代码不是100%一样,很简单,只是为了测试,请不要介意。在一般情况下,我们避免使用 VisualTree Helper 来完成这项工作,x:Bind 可以用于事件,在这里非常有用。

【讨论】:

  • 谢谢格蕾丝冯。我决定使用下面 Evk 的命令方法,主要是因为我想使用命令,但感谢您的帮助!
【解决方案3】:

如果你想纯粹使用 x:Bind,我建议你在 XAML 中定义 Click 事件,Click="onButtonClicked"。在 View 中,您调用 ViewModel(在 MVVM 中,View 知道 VM)或者您可以发布消息,ViewModel 将捕获它并完成工作。 MVVM Light 有这种 Messenger 机制。

【讨论】:

  • 无论我使用 FindName 在 ViewModel 中调用 ListViewItem,还是使用 SelectionChanged 在 View 的代码中调用,或者使用 GetFocus 在 Textbox 本身上调用,ListviewItem.Content 总是返回 null。我想我正在寻找的是返回当前选择或聚焦的 ListViewItem 内容的最佳实践。
  • 内容是指绑定到列表视图项的对象还是列表视图项的 xaml 内容?如果要访问该对象,可以从事件处理程序中的发送者对象的DataContext 中获取。您还可以使用 x:Bind 绑定到 ViewModel 中的事件处理程序,这样您就不需要再处理 View 中的事件了。
猜你喜欢
  • 2013-01-01
  • 2012-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多