如何从数据模板内部绑定到 ViewModel 上的属性?



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

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

下面是视图模型中的适用代码。

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

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

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( 应用视图模型类:

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类是实现 InotifyPropertyChanged Interface.

我对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方面的经验。

我终于得到了这个。要从数据模板中绑定到视图模型上的命令,我必须使用以下方法:

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

定位器是指视图模型定位器。(注意:我正在使用 MVVMLight。

我稍微修改了BindableFlyout类,所以它支持DataTempalteSelector.

  1. 添加Margin(-14, -8, -14, -8),使其看起来更像MenuFlyout
  2. 重用ItemsControl,以便在更改值时更有效率。
  3. flyout分配给Tag,否则无法在 Child 的事件处理程序中找到浮出控件。 用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);
}
}

最新更新