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

时间:2019-09-10 19:40:27

标签: c# xaml uwp mvvm-light

我无法将MenuFlyoutItem的命令绑定到ViewModel上的RelayCommand。我已经尝试了所有可以想到的方法,例如ElementName,RelativeSource等。有人可以告诉我我做错了什么吗?下面的代码中显示的其他两个绑定有效。只是命令绑定没有。我的意思是我已经在RelayCommand调用的OnFilterListCommand方法中设置了一个断点。当我单击菜单弹出菜单时,执行将永远不会达到该断点。

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

这是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;
    ...
}

我正在使用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);
    }
}

我在这里找到它:http://blog.jerrynixon.com/2013/12/xaml-how-to-add-itemssource-to-windows.html

4 个答案:

答案 0 :(得分:0)

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

“ thePage”是视图或视图模型的名称吗?显然,如果不是您的视图模型,那么它将无法正常工作;如果是,则一旦在xaml文件或后面的代码中将其绑定到您的数据上下文会更容易。

更新

我想我明白了您要做什么。有多种方法可以将参数传递给命令,但是如果我遵循这里的想法,最简单的方法就是只调用命令并在视图模型中使用绑定属性CompanyName。

(在此处存储,因此如果有任何错误,将对其进行编辑)。

public RelayCommand FilterListCommand;

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

请尝试确保CompanyName是在您的视图模型中正确定义的公共属性,该属性会引发属性更改。

说句公道话,我发现了一些与您的原始示例相似的人绑定示例,这可能是该平台常见的新习惯用法。只是在这里尝试利用我在Silverlight / WPF中的经验。

答案 1 :(得分:0)

这是Windows 10中可用的最直接的数据绑定的示例( x:Bind 使用 compile binding ,可在编译时发现错误并提高性能) :

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)模型类别:

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

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

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

编辑:当然,BaseBind类是taht实现的类。   INotifyPropertyChanged接口。

答案 2 :(得分:0)

我终于明白了。要从数据模板中绑定到视图模型上的命令,我必须使用以下内容:

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

Locator引用ViewmodelLocator。 (注意:我正在使用MVVMLight。)

答案 3 :(得分:0)

我对Public WithEvents appevent As Application ''New Code Private Sub appevent_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range) ''New Code Dim Count As Integer Count = 0 While Count < 1 And ReadMode = True Count = 2 'https://www.excel-easy.com/vba/examples/highlight-active-cell.html Dim rowNumberValue As Integer, columnNumberValue As Integer, i As Integer, j As Integer Cells.Interior.ColorIndex = 0 rowNumberValue = ActiveCell.Row columnNumberValue = ActiveCell.Column For i = 1 To rowNumberValue Cells(i, columnNumberValue).Interior.ColorIndex = 37 Next i For j = 1 To columnNumberValue Cells(rowNumberValue, j).Interior.ColorIndex = 37 Next j Wend End Sub 类进行了一些修改,因此它支持Public myobject As New Class1 ''New Code Public ReadMode As Boolean 'Public Sub ReadModeToggle(control As IRibbonControl) Public Sub ReadModeToggle() Set myobject.appevent = Application ''New Code If ReadMode = False Then Call ReadModeEnable_Sub ElseIf ReadMode = True Then Call ReadModeDisable_Sub End If Debug.Print "ReadMode = " & ReadMode End Sub Public Sub ReadModeEnable_Sub() ReadMode = True End Sub Public Sub ReadModeDisable_Sub() ReadMode = False Dim rowNumberValue As Integer, columnNumberValue As Integer, i As Integer, j As Integer Cells.Interior.ColorIndex = 0 rowNumberValue = ActiveCell.Row columnNumberValue = ActiveCell.Column For i = 1 To rowNumberValue Cells(i, columnNumberValue).Interior.ColorIndex = 0 Next i For j = 1 To columnNumberValue Cells(rowNumberValue, j).Interior.ColorIndex = 0 Next j End Sub

  1. 添加BindableFlyout,使它看起来更像DataTempalteSelector
  2. 重新使用Margin(-14, -8, -14, -8),以便在更改值时更加高效。
  3. MenuFlyout分配给ItemsControl,否则将无法在孩子的事件处理程序中找到弹出窗口。使用flyout
Tag
var flyout = (Flyout)((((DependencyObject)sender).GetParents().FirstOrDefault(obj => obj is ItemsControl)).Tag);