1

在我的 Viewmodel 中,我具有LoggedInAs字符串EditMode类型和布尔类型的属性。我还有一个名为 List 的属性ReaderList,我将其绑定到 ItemsControl 用于显示目的,如下所示:

<ItemsControl Name="ReaderList" ItemTemplateSelector="{StaticResource drts}"/>

我使用的是 Caliburn.Micro,所以绑定是通过命名自动完成的。我想使用 DataTemplateSelector 因为如果应用程序处于 EditMode 并且 Person 是登录的人,我想要一个完全不同的显示。所以这是我对资源的声明,

<UserControl.Resources>
    <DataTemplate x:Key="OtherPersonTemplate"> ... </DataTemplate>
    <DataTemplate x:Key="CurrentUserIsPersonTemplate"> ...  </DataTemplate>

    <local:DisplayReaderTemplateSelector x:Key="drts" 
           IsLoggedInAs="{Binding LoggedInAs}" 
           IsEditMode="{Binding EditMode}" 
           CurrentUserTemplate="{StaticResource CurrentUserIsPersonTemplate}"
           OtherUserTemplate="{StaticResource OtherPersonTemplate}"/>
</UserControl.Resources>

这里是类的代码:

public class DisplayReaderTemplateSelector: DataTemplateSelector {
    public DataTemplate CurrentUserTemplate { get; set; }
    public DataTemplate OtherUserTemplate { get; set; }

    public string IsLoggedInAs {get; set;}
    public bool IsEditMode { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container){
        var _r = item as Person;
        if (IsEditMode && _r.Name == IsLoggedInAs) return CurrentUserTemplate;
        else return OtherUserTemplate;
    }
}

由于某种原因,应用程序在实例化 Viewmodel(分别是 View)时崩溃。错误在哪里,和/或我该如何解决这个问题?

编辑:崩溃是由于 DisplayReaderTemplateSelector 的构造中的绑定表达式 - 因为IsLoggedIn并且EditMode不是 DependencyProperties。

所以现在的问题是:如果我不能绑定到值,我怎么能有一个依赖于 ViewModel 状态的 DataTemplateSelector?

4

1 回答 1

1

虽然您可以使用DataTemplateSelector类似的东西,但您可能不会惊讶地发现在 Caliburn.Micro 中以View.ContextViewLocator

在您的 VM 上,您可以创建一个属性,该属性提供 CM 将用于解析视图的上下文字符串 - 因为它使用命名约定,您只需要为子视图提供正确的命名空间/名称以及它的上下文字符串找到替代视图

在您的 VM 中,您可以创建一个使用用户详细信息来确定其值的上下文属性:

IE

public class SomeViewModel
{
    public string Context 
    {
        get 
        { 
            if (IsEditMode && _r.Name == IsLoggedInAs) return "Current";
            else return "Other";
        }
    }  

    // ... snip other code
}

我看到的唯一问题(可能有解决方法)是您想从 a 内部确定视图ViewModel- 通常您确定更高的上下文并将其传递给 aContentControl并且 CM 在定位该 VM 的视图时使用它

例如

你的主虚拟机:

public class MainViewModel
{
    public SomeSubViewModel { get; set; } // Obviously would be property changed notification and instantiation etc, I've just left it out for the example
}

和相关视图

<UserControl>
    <!-- Show the default view for this view model -->
    <ContentControl x:Name="SomeSubViewModel" />
    <!-- Show an alternative view for this view model -->
    <ContentControl x:Name="SomeSubViewModel" cal:View.Context="Alternative" />
</UserControl>

那么您的 VM 命名结构将是:

- ViewModels
|
----- SomeSubViewModel.cs
    |
    - SomeSubView.xaml
    |
    - SomeSubView
    |
    ----- Alternative.xaml

CM 会知道在SomeSubView命名空间中查找Alternative基于原始 VM 名称和Context属性调用的控件(SomeSubViewModel 减去 Model 加上点加上ContextSomeSubView.Alternative)

所以我必须玩一玩,因为这是标准的做法。如果您要这样做,您必须创建一个子视图模型并将 a 添加ContentControl到您的视图并将View.Context属性绑定到ContextVM 上的属性,或者将Context属性添加到更高的位置(到父 VM)。

我将看看一些替代方案 - 如果无法让当前 ViewModel 使用标准 CM 根据属性决定其视图,您可以自定义并可能使用提供上下文ViewLocator的接口(IProvideContext 或类似的)ViewLocator立即 -(我认为您不能直接从 VM 连接到视图解析过程)

我很快就会回来提供另一个答案或替代方案!

编辑:

好的,这似乎是最直接的方法。我刚刚创建了一个Context直接从 VM提供的接口

public interface IProvideContext
{
    string Context { get; }
}

然后,如果没有指定上下文,我自定义了ViewLocator实现(您可以在 中执行此操作)以使用它:Bootstrapper.Configure()

ViewLocator.LocateForModel = (model, displayLocation, context) =>
{
    var viewAware = model as IViewAware;

    // Added these 3 lines - the rest is from CM source
    // Try cast the model to IProvideContext
    var provideContext = model as IProvideContext;

    // Check if the cast succeeded, and if the context wasn't already set (by attached prop), if we're ok, set the context to the models context property
    if (provideContext != null && context == null)
         context = provideContext.Context;

    if (viewAware != null)
    {                    
        var view = viewAware.GetView(context) as UIElement;
        if (view != null)
        {
#if !SILVERLIGHT && !WinRT
            var windowCheck = view as Window;
            if (windowCheck == null || (!windowCheck.IsLoaded && !(new WindowInteropHelper(windowCheck).Handle == IntPtr.Zero)))
            {
                LogManager.GetLog(typeof(ViewLocator)).Info("Using cached view for {0}.", model);
                return view;
            }
#else
            LogManager.GetLog(typeof(ViewLocator)).Info("Using cached view for {0}.", model);
            return view;
#endif
        }
    }

    return ViewLocator.LocateForModelType(model.GetType(), displayLocation, context);
};

这应该对您有用,并允许您直接在目标上设置上下文ViewModel- 显然这可能仅适用于 View-First 方法

所以你需要做的就是像我上面展示的那样构建你的视图(正确的命名空间等),然后根据和Context的值在你的VM上设置属性IsLoggedInAsEditMode

于 2013-03-13T11:08:00.057 回答