简体   繁体   English

如何从 DataTemplate 内部绑定到 ViewModel 上的属性?

[英]How do I bind to a property on the ViewModel from inside a DataTemplate?

I am unable to bind the command of a MenuFlyoutItem to a RelayCommand on my ViewModel.我无法将 MenuFlyoutItem 的命令绑定到 ViewModel 上的 RelayCommand。 I've tried every way I could think of, ElementName, RelativeSource, etc. Can anyone show me what I'm doing wrong?我已经尝试了所有我能想到的方法,ElementName、RelativeSource 等。谁能告诉我我做错了什么? The other two bindings shown in the code below work.下面代码中显示的其他两个绑定有效。 It is only the command binding that doesn't.只有命令绑定没有。 I mean I have set a breakpoint in the OnFilterListCommand method that is called by the RelayCommand.我的意思是我在 RelayCommand 调用的 OnFilterListCommand 方法中设置了一个断点。 Execution never reaches that breakpoint when I click on a menu flyout item.当我单击菜单弹出项时,执行永远不会到达该断点。

<wct:DataGridComboBoxColumn.HeaderStyle>
    <Style TargetType="controlsprimitives:DataGridColumnHeader">
        <Setter Property="ContentTemplate"> 
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
                        <TextBlock Text="Company" TextWrapping="Wrap"/>
                        <Button HorizontalAlignment="Right" x:Name="MyButton" Content="test">
                            <i:Interaction.Behaviors>                                                  
                                <core:EventTriggerBehavior EventName="Click">
                                <core:InvokeCommandAction Command="{Binding ElementName=thePage, Path=DataContext.OpenFlyoutCommand}" CommandParameter="{Binding ElementName=MyButton}"/>                                                            
                                 </core:EventTriggerBehavior>                                                       
                            </i:Interaction.Behaviors>
                            <FlyoutBase.AttachedFlyout>
                                <Flyout helpers:BindableFlyout.ItemsSource="{Binding ElementName=theView, Path=DataContext.SourceForCompaniesList}" x:Name="theFlyout">                                                              
                                    <helpers:BindableFlyout.ItemTemplate>                                                               
                                        <DataTemplate>
                                            <MenuFlyoutItem Text="{Binding CompanyName}" Command="{Binding Path=DataContext.FilterListCommand, ElementName=theView}" IsTapEnabled="True"/>                                                                    
                                        </DataTemplate>                                                     
                                    </helpers:BindableFlyout.ItemTempla
                                </Flyout>
                            </FlyoutBase.AttachedFlyout>

Here is the applicable code from the ViewModel.这是来自 ViewModel 的适用代码。

private RelayCommand<object> _filterListCommand;
public RelayCommand<object> FilterListCommand => _filterListCommand
                ?? (_filterListCommand = new RelayCommand<object>(OnFilterListCommand));

private void OnFilterListCommand(object obj)
{
    string selectedCompany = obj as string;
    ...
}

I'm using Jerry Nixon's solution to add an ItemsSources property to FlyoutMenu:我正在使用 Jerry Nixon 的解决方案将 ItemsSources 属性添加到 FlyoutMenu:

public class BindableFlyout : DependencyObject
{
    #region ItemsSource

    public static IEnumerable GetItemsSource(DependencyObject obj)
    {

        return obj.GetValue(ItemsSourceProperty) as IEnumerable;


    }
    public static void SetItemsSource(DependencyObject obj, IEnumerable value)
    {

        obj.SetValue(ItemsSourceProperty, value);

    }
    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.RegisterAttached("ItemsSource", typeof(IEnumerable),
        typeof(BindableFlyout), new PropertyMetadata(null, ItemsSourceChanged));
    private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    { Setup(d as Windows.UI.Xaml.Controls.Flyout); }

    #endregion

    #region ItemTemplate

    public static DataTemplate GetItemTemplate(DependencyObject obj)
    {
        return (DataTemplate)obj.GetValue(ItemTemplateProperty);
    }
    public static void SetItemTemplate(DependencyObject obj, DataTemplate value)
    {
        obj.SetValue(ItemTemplateProperty, value);
    }
    public static readonly DependencyProperty ItemTemplateProperty =
        DependencyProperty.RegisterAttached("ItemTemplate", typeof(DataTemplate),
        typeof(BindableFlyout), new PropertyMetadata(null, ItemsTemplateChanged));
    private static void ItemsTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    { Setup(d as Windows.UI.Xaml.Controls.Flyout); }

    #endregion

    private static async void Setup(Windows.UI.Xaml.Controls.Flyout m)
    {
        if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
            return;
        var s = GetItemsSource(m);
        if (s == null)
            return;
        var t = GetItemTemplate(m);
        if (t == null)
            return;
        var c = new Windows.UI.Xaml.Controls.ItemsControl
        {
            ItemsSource = s,
            ItemTemplate = t,
        };
        var n = Windows.UI.Core.CoreDispatcherPriority.Normal;
        Windows.UI.Core.DispatchedHandler h = () => m.Content = c;
        await m.Dispatcher.RunAsync(n, h);
    }
}

I got it here: http://blog.jerrynixon.com/2013/12/xaml-how-to-add-itemssource-to-windows.html我在这里得到它: http : //blog.jerrynixon.com/2013/12/xaml-how-to-add-itemssource-to-windows.html

I don't know anything about MenuFlyoutItem, but normally a control only needs the following:我对 MenuFlyoutItem 一无所知,但通常控件只需要以下内容:

    <FlyoutBase.AttachedFlyout>
     <Flyout helpers:BindableFlyout.ItemsSource="{Binding ElementName=thePage, Path=DataContext.SourceForCompaniesList}" x:Name="theFlyout">                                                              
        <helpers:BindableFlyout.ItemTemplate>                                                               
            <DataTemplate>
                <MenuFlyoutItem Text="{Binding CompanyName}" Command="{Binding FilterListCommand}" IsTapEnabled="True"/>                                                                    
            </DataTemplate>                                                     
        </helpers:BindableFlyout.ItemTemplate>
     </Flyout>
</FlyoutBase.AttachedFlyout>

Is "thePage" the name of your view or view model? “thePage”是您的视图或视图模型的名称吗? Obviously if it isn't your view model then this isn't going to work, and if it is, it would be easier to bind it to your data context once in your xaml file or in the code behind.显然,如果它不是您的视图模型,那么这将不起作用,如果是,则在您的 xaml 文件或后面的代码中将其绑定到您的数据上下文会更容易。

Update:更新:

I think I see what you're trying to do.我想我明白你在做什么。 There are ways to pass parameters to a command, but if I follow the idea here, the easiest thing would be to just invoke the command and use the bound property CompanyName in the view model.有多种方法可以将参数传递给命令,但如果我遵循这里的想法,最简单的方法就是调用命令并在视图模型中使用绑定属性 CompanyName。

(Going on memory here, so will edit if there are any mistakes). (在这里继续记忆,所以如果有任何错误会编辑)。

public RelayCommand FilterListCommand;

FilterListCommand = new RelayCommand(() =>
{
    string selectedCompany = obj as string;  
    MessageBox.Show(selectedCompany); 
});

Give that a try and make sure CompanyName is a properly defined public property in your view model that raises property changed.试一试并确保 CompanyName 是您的视图模型中正确定义的公共属性,该属性引发属性更改。

To be fair, I found a few examples of people binding similar to your original example which may be a new idiom common to the platform.公平地说,我发现了一些与您的原始示例类似的人员绑定示例,这可能是该平台常见的新习语。 Just trying to use my experience with Silverlight/WPF here.只是想在这里使用我在 Silverlight/WPF 方面的经验。

This is an example of the most direct data binding available in Windows 10 ( x:Bind uses compile binding , that finds errors already at compile time and boosts performances):这是 Windows 10 中可用的最直接数据绑定的示例( x:Bind使用compile binding ,它在编译时发现错误并提高性能):

1) XAML page: 1) XAML 页面:

<ListView ItemsSource="{x:Bind ViewModel.ItemsList, Mode=OneWay}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="SampleObjectClass">
            <TextBlock Text="{x:Bind Title}"/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

2) Model class: 2)模型类:

public class SampleObjectClass
{
    public String Title { get; set; }
}

3) MainPage.xaml.cs: 3) MainPage.xaml.cs:

public sealed partial class MainPage : Page
{
    public AppViewModel ViewModel { get; set; } = new AppViewModel();
    public static MainPage Current;
    // The "Current" property is necessary in order to retriew the singleton istance of the AppViewModel in the entire app... thus we are using the MainPage as a "shell" structure for the app


    public MainPage()
    {
        Current = this;

        // Other things...
    }

    // Other stuff...
}

4) AppViewModel class: 4) AppViewModel 类:

public class AppViewModel : BaseBind
{
    public AppViewModel()
    {
        ItemsList = new ObservableCollection<ItemsList >();
        ItemsList .CollectionChanged += (sender, e) =>
        {
            // Do something if the list changes (e.g. update another property in the viewmodel class)...
        };
    }

    public ObservableCollection<SampleObjectClass> ItemsList { get; set; }
}

EDIT: of course, the BaseBind class is the class taht implements the INotifyPropertyChanged Interface.编辑:当然,BaseBind 类是实现 INotifyPropertyChanged 接口的类。

I finally got this.我终于得到了这个。 To bind to the command on the viewmodel from within the datatemplate, I had to use the following:要从数据模板中绑定到视图模型上的命令,我必须使用以下命令:

<MenuFlyoutItem Text="{Binding CompanyName}" 
                Command="{Binding CompaniesListViewModel.CompanyListCommand, Source={StaticResource Locator}}" 
                CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />

Locator refers to the ViewmodelLocator. Locator 指的是 ViewmodelLocator。 (Note: I am using MVVMLight.) (注意:我使用的是 MVVMLight。)

I modified the BindableFlyout class a bit, so it supports DataTempalteSelector .我稍微修改了BindableFlyout类,因此它支持DataTempalteSelector

  1. add Margin(-14, -8, -14, -8) so make it look more like a MenuFlyout .添加Margin(-14, -8, -14, -8)使其看起来更像MenuFlyout
  2. re-use the ItemsControl so it's more efficient if you're changing the values.重新使用ItemsControl以便在更改值时效率更高。
  3. assign flyout to Tag , other otherwise there's no way you can find the flyout in child's event handler.flyout分配给Tag ,否则您无法在孩子的事件处理程序中找到浮出控件。 do it with var flyout = (Flyout)((((DependencyObject)sender).GetParents().FirstOrDefault(obj => obj is ItemsControl)).Tag);使用var flyout = (Flyout)((((DependencyObject)sender).GetParents().FirstOrDefault(obj => obj is ItemsControl)).Tag);
internal static class DependencyObjectExtension
{
    internal static IEnumerable<DependencyObject> GetParents(this DependencyObject child)
    {
        var parent = VisualTreeHelper.GetParent(child);
        while (parent != null)
        {
            yield return parent;
            child = parent;
            parent = VisualTreeHelper.GetParent(child);
        }
    }
}
public class BindableFlyout : DependencyObject
{
    #region ItemsSource

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.RegisterAttached("ItemsSource", typeof(IEnumerable), typeof(BindableFlyout), new PropertyMetadata(null, OnDependencyPropertyChanged));

    public static IEnumerable GetItemsSource(DependencyObject obj) =>
        obj.GetValue(ItemsSourceProperty) as IEnumerable;
    public static void SetItemsSource(DependencyObject obj, IEnumerable value) =>
        obj.SetValue(ItemsSourceProperty, value);

    #endregion

    #region ItemTemplate

    public static readonly DependencyProperty ItemTemplateProperty =
        DependencyProperty.RegisterAttached("ItemTemplate", typeof(DataTemplate), typeof(BindableFlyout), new PropertyMetadata(null, OnDependencyPropertyChanged));

    public static DataTemplate GetItemTemplate(Flyout obj) =>
        (DataTemplate)obj.GetValue(ItemTemplateProperty);
    public static void SetItemTemplate(Flyout obj, DataTemplate value) =>
        obj.SetValue(ItemTemplateProperty, value);

    #endregion

    #region ItemTemplateSelector

    public static readonly DependencyProperty ItemTemplateSelectorProperty =
        DependencyProperty.RegisterAttached("ItemTemplateSelector", typeof(DataTemplateSelector), typeof(BindableFlyout), new PropertyMetadata(null, OnDependencyPropertyChanged));

    public static DataTemplateSelector GetItemTemplateSelector(Flyout obj) =>
        (DataTemplateSelector)obj.GetValue(ItemTemplateSelectorProperty);
    public static void SetItemTemplateSelector(Flyout obj, DataTemplate value) =>
        obj.SetValue(ItemTemplateSelectorProperty, value);

    #endregion

    private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var flyout = (Flyout)d;
        var items = flyout.Content as ItemsControl;
        if (items == null)
            items = new ItemsControl() { Margin = new Thickness(-14, -8, -14, -8) };
        void callback()
        {
            flyout.Content = items;
            items.ItemsSource = GetItemsSource(flyout);
            items.ItemTemplate = GetItemTemplate(flyout);
            items.ItemTemplateSelector = GetItemTemplateSelector(flyout);
            items.Tag = flyout;
        }
        var dispatcher = Application.Current.Dispatcher;
        if (dispatcher.CheckAccess())
        {
            callback();
            return;
        }
        dispatcher.Invoke(callback, DispatcherPriority.Normal);
    }
}

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

相关问题 如何从 DataTemplate 绑定到 ViewModel 属性 - How can you bind to a ViewModel property from a DataTemplate 如何将DataTemplate宽度绑定到父对象并能够更新viewmodel属性? - How to bind DataTemplate width to parent and be able to update viewmodel property? WinUI 3 使用 DataTemplate 时如何将命令绑定到 ViewModel 属性? - WinUI 3 How to Bind Command to ViewModel property when using a DataTemplate? x:将ViewModel方法绑定到DataTemplate内部的事件 - x:Bind ViewModel method to an Event inside DataTemplate x:将 ViewModel RelayCommand 绑定到 DataTemplate 中的命令 - x:Bind ViewModel RelayCommand to a command inside DataTemplate 如何将 ViewModel 中的命令绑定到不属于自己的文件中的 DataTemplate - How to bind command from ViewModel to a DataTemplate which is in in't own file 如何将图像按钮命令从DataTemplate选择器绑定到ViewModel? - How to bind Image Button command from a DataTemplate Selector to a ViewModel? 如何在资源字典中绑定datatemplate - How do I bind datatemplate in a resource dictionary 如何从DataTemplate的父ViewModel类获取属性 - How to get property from the DataTemplate's parent ViewModel class 如何以编程方式绑定DataTemplate内部控件的(依赖)属性? - How to programmatically bind a (dependency) property of a control that's inside a DataTemplate?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM