我基本上问的问题与this person相同,但是在较新的x:Bind
的背景下。
ViewModels的DataContext定义如下
<Page.DataContext>
<vm:ChapterPageViewModel x:Name="ViewModel" />
</Page.DataContext>
因此,每当我需要绑定某些内容时,我就会明确地对ViewModel这样做
ItemsSource="{x:Bind ViewModel.pageList, Mode=OneWay}"
然而,这在模板中不起作用
<FlipView ItemsSource="{x:Bind ViewModel.pageList, Mode=OneWay}">
<FlipView.ItemTemplate>
<DataTemplate x:DataType="models:Image">
<ScrollViewer SizeChanged="{x:Bind ViewModel.PageResized}"> <-- this here is the culprit
<Image Source="{x:Bind url}"/>
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
阅读文档,我发现使用Path
基本上应该将上下文重置为页面,但是这个(x:Bind Path=ViewModel.PageResizeEvent
也不起作用。我仍然得到Object reference not set to an instance of an object
,这应该意味着它没有看到方法(但是为空)。
图片类:
public class Image {
public int page { get; set; }
public string url { get; set; }
public int width { get; set; }
public int heigth { get; set; }
}
并在ChapterPageViewModel
中private List<Image> _pageList;
public List<Image> pageList {
get { return _pageList; }
set { Set(ref _pageList, value); }
}
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode,
IDictionary<string, object> suspensionState)
{
Initialize();
await Task.CompletedTask;
}
private async void Initialize()
{
pageList = await ComicChapterGet.GetAsync(_chapterId);
}
public void PageResized(object sender, SizeChangedEventArgs e)
{
//resizing logic happens here
}
答案 0 :(得分:3)
我们在这里有两个问题:
首先,尝试将事件直接绑定到事件处理程序委托
这根本不会奏效,简单地说
处理MVVM模式事件的一种方法是使用EventTrigger and ICommand.
它需要一个实现ICommand的类。如果不知道如何操作,This post会对您有所帮助。我会打电话给我DelegateCommand
。
以下是我将如何通过两个步骤重构它:
1)向VM添加命令:
public class ChapterPageViewModel
{
public ChapterPageViewModel()
{
this.PageResizedCommand = new DelegateCommand(OnPageResized);
}
public DelegateCommand PageResizedCommand { get; }
private void OnPageResized()
{ }
}
2)使用EventTrigger和InvokeCommandAction将该命令绑定到SizeChanged事件。
<Page (...)
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core">
(...)
<FlipView ItemsSource="{x:Bind ViewModel.pageList, Mode=OneWay}" >
<FlipView.ItemTemplate>
<DataTemplate x:DataType="models:Image">
<ScrollViewer>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SizeChanged">
<core:InvokeCommandAction
Command="{x:Bind ViewModel.PageResizedCommand }" />
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
<Image Source="{x:Bind url}"/>
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Page>
“但是加布里埃尔”,你说,“那不起作用!”
我知道!这是因为第二个问题,试图x:绑定一个不属于DataTemplate类的属性
这个与this question密切相关,所以我会从那里借一些信息。
来自MSDN,关于DataTemplate and x:Bind
在DataTemplate内部(无论是否用作项目模板,内容 模板或标题模板),不解释Path的值 在页面的上下文中,但在数据对象的上下文中 模仿。这样它的绑定就可以得到验证(并且有效 为它们生成的代码)在编译时,DataTemplate需要 使用x:DataType声明其数据对象的类型。
因此,当您执行<ScrollViewer SizeChanged="{x:Bind ViewModel.PageResized}">
时,您实际上是在models:Image
类上搜索名为ViewModel的属性,该类是DataTemplate的x:DataType
。并且该类不存在这样的属性。
在这里,我可以看到两个选项。 选择其中一个:
将ViewModel添加为Image类的属性,并将其填充到VM上。
public class Image {
(...)
public ChapterPageViewModel ViewModel { get; set; }
}
public class ChapterPageViewModel
{
(...)
private async void Initialize() {
pageList = await ComicChapterGet.GetAsync(_chapterId);
foreach(Image img in pageList)
img.ViewModel = this;
}
}
只有这一点,以前的代码应该无需更改任何其他内容。
删除x:绑定并使用ElementName返回到良好的ol'Binding。
<FlipView ItemsSource="{x:Bind ViewModel.pageList, Mode=OneWay}" x:Name="flipView">
<FlipView.ItemTemplate>
<DataTemplate x:DataType="models:Image">
<ScrollViewer>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SizeChanged">
<core:InvokeCommandAction
Command="{Binding DataContext.PageResizedCommand
, ElementName=flipView}" />
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
<Image Source="{x:Bind url}"/>
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
这种方式会破坏你的问题的目的,但它确实有效,而且比以前更容易实现。