在我的Viewmodel中,我有类型为string的属性LoggedInAs
和bool类型的EditMode
。我还有一个名为ReaderList
的List属性,我将其绑定到ItemsControl以进行显示,如下所示:
<ItemsControl Name="ReaderList" ItemTemplateSelector="{StaticResource drts}"/>
我正在使用Caliburn.Micro,因此命名会自动完成Binding。我想使用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呢?
答案 0 :(得分:1)
虽然您可以使用DataTemplateSelector
或类似的东西,但在Caliburn中找到它可能不会让您感到惊讶.Micro以View.Context
的形式内置了此功能ViewLocator
在您的VM上,您可以创建一个属性,该属性提供CM将用于解析View的上下文字符串 - 因为它使用命名约定,您只需要为子视图提供正确的命名空间/名称以及上下文字符串,用于查找备用视图
在您的VM中,您可以创建一个上下文属性,该属性使用用户详细信息来确定其值:
即
public class SomeViewModel
{
public string Context
{
get
{
if (IsEditMode && _r.Name == IsLoggedInAs) return "Current";
else return "Other";
}
}
// ... snip other code
}
我看到的唯一问题(可能有一个解决方法)是你要从ViewModel
内部确定视图 - 通常你确定更高的上下文并将其传递给ContentControl
并且CM在查找该VM的视图时使用它
e.g。
您的主要虚拟机:
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会知道根据原始VM名称和SomeSubView
属性(SomeSubViewModel减去模型加点加{{1}在Alternative
命名空间中查找名为Context
的控件这是SomeSubView.Alternative)
所以我必须要玩,因为这是标准的做法。如果您这样做,则必须创建子视图模型并向视图添加Context
并将ContentControl
属性绑定到VM上的View.Context
属性,或者将Context
属性添加到更高的位置(到父虚拟机)。
我会看一些替代方案 - 如果没有办法让当前的ViewModel根据使用标准CM的属性来决定其视图,您可以自定义Context
并可能使用接口(IProvideContext或somesuch)它立即为ViewLocator
提供了一个上下文 - (我认为你不能直接从VM中查看解析过程)
我很快会回来另一个答案或替代方案!
编辑:
好的,这似乎是最简单的方法。我刚创建了一个直接从VM提供ViewLocator
的接口
Context
然后我自定义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);
};
上设置上下文 - 显然这可能仅适用于View-First方法
所以你需要做的就是构建你上面显示的视图(正确的命名空间等),然后根据ViewModel
和{{1}的值在你的VM上设置Context
属性}