【问题标题】:Wrong type in ListDetailsView DetailsTemplateListDetailsView DetailsTemplate 中的类型错误
【发布时间】:2021-05-07 15:43:30
【问题描述】:

我正在使用 Windows 社区工具包开发 UWP 应用。在某些页面中,我像这样使用ListDetailsView (FKA MasterDetailsView):

<!-- page and stuff... -->
<controls:ListDetailsView
         x:Name="ListViewParameters"
         ItemsSource="{x:Bind ViewModel.ParametersList, Mode=OneWay}"
         SelectedItem="{x:Bind ViewModel.Selected, Mode=TwoWay}"
         CompactModeThresholdWidth="720"
         BorderBrush="Transparent"
         NoSelectionContent="Please select a parameter to view"
         ListPaneBackground="{ThemeResource SystemControlPageBackgroundChromeLowBrush}"
         ListPaneWidth="350"
         SelectionChanged="ListViewParameters_SelectionChanged">

    <controls:ListDetailsView.ItemTemplate>
        <DataTemplate x:DataType="parameters:ParameterSet">
            <!-- item template, works great -->
        </DataTemplate>
    </controls:ListDetailsView.ItemTemplate>

    <controls:ListDetailsView.DetailsTemplate>
        <DataTemplate>
            <ScrollViewer x:Name="ContentScrollViewer">
                <StackPanel x:Name="Details" Margin="{StaticResource MediumLeftMargin}">
                    <!-- this is where problems happen -->
                    <TextBlock Style="{StaticResource TitleTextBlockStyle}"
                              Text="{Binding Name, Mode=OneWay}"
                              x:Name="title"
                              Margin="{StaticResource MediumBottomMargin}"/>
                    <!-- other elements with bindings... -->
                </StackPanel>
            </ScrollViewer>
        </DataTemplate>
   </controls:ListDetailsView.DetailsTemplate>
</controls:ListDetailsView>

页面加载时出现我的问题:绑定到DetailsTemplate 的第一件事似乎是页面的DataContext,即ViewModel。我可以在输出控制台中看到这一点:

Error: BindingExpression path error: 'Name' property not found on 'MyProject.ParametersViewModel'. BindingExpression: Path='ParameterSet.Name' DataItem='MyProject.Components.ParametersEditor'; target element is 'Windows.UI.Xaml.Controls.TextBox' (Name='null'); target property is 'Text' (type 'String')

Error: BindingExpression path error: 'IsDefault' property not found on 'MyProject.ParametersViewModel'. BindingExpression: Path='ParameterSet.IsDefault' DataItem='MyProject.Components.ParametersEditor'; target element is 'Windows.UI.Xaml.Controls.TextBox' (Name='null'); target property is 'IsEnabled' (type 'Boolean')

...

这些绑定错误会导致页面加载过程中出现一些延迟(超过 1 秒,从而导致糟糕的用户体验),如果我尝试在 DetailsTemplate 上设置 DataType,它会因转换异常而崩溃。

下面是代码:

public sealed partial class ParametersPage : Page
{
    public ParametersPage()
    {
        this.InitializeComponent();
        Loaded += ParametersPage_Loaded;
        Unloaded += ParametersPage_Unloaded;
    }

    public ParametersViewModel ViewModel { get; set; } = new ParametersViewModel();

    private async void ParametersPage_Loaded(object sender, RoutedEventArgs e)
    {
        await ViewModel.LoadParametersAsync();
    }

    private async void ParametersPage_Unloaded(object sender, RoutedEventArgs e)
    {
        await ViewModel.UpdateParameterSetAsync(ViewModel.Selected);
    }

    private async void ListViewParameters_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.RemovedItems.Count > 0 && e.RemovedItems[0] is ParameterSet parameters)
        {
                await ViewModel.UpdateParameterSetAsync(parameters);
        }
    }
}

ParametersViewModel 看起来像这样:

public class ParametersViewModel : Observable
{
    public ParametersViewModel()
    {
        ParametersList = new ObservableCollection<ParameterSet>();
    }

    public ObservableCollection<ParameterSet> ParametersList
    {
        get { return _parameters; }
        set { Set(ref _parameters, value); }
    }

    // some other methods like LoadParametersAsync and UpdateParameterSetAsync


    private ParameterSet _selected;

ParameterSet 包含一个字符串属性Name 和其他一些东西。

我的代码有问题吗?如何避免将DataContext 绑定到此模板?

注意:我也在Windows Community Toolkit discussion on GitHub上发了一个问题,但是那里几乎没有流量。

更新:添加DataType 时发生崩溃,因为我没有将{Binding} 更改为{x:Bind}。它不再崩溃,但调试控制台中仍然记录了一个异常:

Exception thrown at 0x00007FF94A6F4B59 (KernelBase.dll) in MyApp.exe: WinRT originate error - 0x80004002 : 'System.InvalidCastException: Unable to cast object of type 'MyProject.ViewModels.ShellViewModel' to type 'MyProject.Core.Models.ParameterSet'.
   at MyProject.Views.ParametersPage.ParametersPage_obj12_Bindings.SetDataRoot(Object newDataRoot)
   at MyProject.Views.ParametersPage.ParametersPage_obj12_Bindings.DataContextChangedHandler(FrameworkElement sender, DataContextChangedEventArgs args)'.

更新 2: 我忘了说我的项目是用Windows Template Studio 生成的,ShellViewModel 是通用视图模型。可以通过生成具有一个 ListDetails 页面的 WTS 项目来重现该问题。

ListDetailsPage.xaml 中,将DetailsTemplate 更改为:

<DataTemplate x:Key="DetailsTemplate" x:DataType="model:SampleOrder">
    <Grid>
        <views:MasterDetailDetailControl MasterMenuItem="{x:Bind}" />
    </Grid>
</DataTemplate>

应该抛出一个转换异常。

【问题讨论】:

    标签: c# xaml uwp windows-community-toolkit


    【解决方案1】:

    The official document 提到“在 DataTemplate 中,Path 的值不是在页面上下文中解释的,而是在被模板化的数据对象的上下文中解释的。在数据模板中使用 {x:Bind} 时,为了在编译时验证其绑定,DataTemplate 需要使用 x:DataType 声明其数据对象的类型。”

    <DataTemplate x:Key="SimpleItemTemplate" x:DataType="data:SampleDataGroup">
        <StackPanel Orientation="Vertical" Height="50">
          <TextBlock Text="{x:Bind Title}"/>
         <TextBlock Text="{x:Bind Description}"/>
        </StackPanel>
      </DataTemplate>
    

    如上所示,该示例可用作绑定到 SampleDataGroup 对象集合的项控件的 ItemTemplate。

    在您的场景中,您需要为 ListDetailsView.DetailsTemplate 的 DataTemplate 指定x:DataType,假设您指定的类型是包含Name 属性的local:ParaClass 类。然后需要将 ParaClass 对象的集合绑定到 ListDetailsView ItemSource。最后,您可以使用{x:Bind} 绑定DataTemplate 中的Name 属性。

    更新:

    我创建了一个 WTS 项目并重现了您的问题。

    <views:ListDetailDetailControl ListMenuItem="{Binding}" />
    

    如您所见,ListDetailDetailControl 绑定了 SampleOrder 对象。因此,其内部控制可以使用&lt;TextBlock Text="{x:Bind ListMenuItem.OrderDate, Mode=OneWay}" /&gt;来实现绑定。

    你修改了原来的模板,直接把这些内部控件放到DetailsTemplate里面,但是你没有设置它的绑定对象,导致这些使用{Binding property}的内部控件找不到正确的DataContext。 因此,您需要为这些内部控件设置DataContext,然后您可以使用{Binding property}直接为这些内部控件的属性设置绑定。请参考以下代码。

    <DataTemplate x:Key="DetailsTemplate">
                <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{Binding}">
                    <ScrollViewer..>
                       ……                         
                                <TextBlock Style="{StaticResource DetailBodyBaseMediumStyle}" Text="{Binding Status, Mode=OneWay}" />                  
                                <TextBlock Style="{StaticResource DetailBodyBaseMediumStyle}" Text="{Binding OrderDate, Mode=OneWay}" /> 
    <TextBlock Style="{StaticResource DetailBodyBaseMediumStyle}" Text="{Binding Company, Mode=OneWay}" />
    ……
                    </ScrollViewer>
                </Grid>
            </DataTemplate>
    

    【讨论】:

    • 我想我发现了我的一个错误:当我尝试将DataType 添加到DataTemplate 时,我忘记将{Binding} 更改为{x:Bind}。我仍然在调试输出控制台中的ParametersPage_obj12_Bindings.SetDataRoot(Object newDataRoot) 处看到InvalidCastException,但我想这是第一次机会异常吧?
    • 绑定属性的类型需要与元素属性对应。比如TextBlock.Text,需要绑定一个字符串属性,而CheckBox.IsChecked属性需要绑定布尔类型属性。请阅读有关Converter的文档。
    • 如果问题还没有解决,可以上传后面的代码。
    • 是的,我就是这么做的。我将编辑我的问题以添加后面的代码
    • 有ShellViewModel吗?根据以上信息,我找不到 ShellViewModel 是做什么的?您能否通过 OneDrive 或 GitHub 向我们提供minimal reproducible example
    猜你喜欢
    • 2021-11-24
    • 1970-01-01
    • 2023-04-03
    • 2012-01-06
    • 2014-03-09
    • 2017-12-30
    • 2013-10-10
    • 2011-12-19
    • 1970-01-01
    相关资源
    最近更新 更多