[英]How to make this DataTemplateSelector work?
In my Viewmodel I have the properties LoggedInAs
of type string and EditMode
of type bool. 在我的Viewmodel中,我具有字符串类型的LoggedInAs
属性和布尔型的EditMode
属性。 I also have a List property called ReaderList
which I bind to an ItemsControl for display purposes like this: 我还有一个名为ReaderList
的List属性,该属性绑定到ItemsControl以便显示,如下所示:
<ItemsControl Name="ReaderList" ItemTemplateSelector="{StaticResource drts}"/>
I am using Caliburn.Micro, so the Binding is done automatically by the naming. 我正在使用Caliburn.Micro,因此绑定是通过命名自动完成的。 I want to use a DataTemplateSelector because if the application is in EditMode and the Person is the one that is logged in I want a fundamentally different display. 我要使用DataTemplateSelector,因为如果应用程序处于EditMode且Person是已登录的应用程序,则我希望得到一种根本不同的显示。 So here is my declaration of the resources, 这是我对资源的声明,
<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>
and here the code for the class: 这里是该类的代码:
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;
}
}
For some reason the application crashes while instantiating the Viewmodel (resp. the View). 由于某种原因,应用程序在实例化Viewmodel(即View)时崩溃。 Where is the error, and/or how could I solve this problem alternatively? 错误在哪里,和/或我该如何解决这个问题?
EDIT: The Crash was due to the binding expressions in the construction of the DisplayReaderTemplateSelector - because IsLoggedIn
and EditMode
are not DependencyProperties. 编辑:崩溃是由于DisplayReaderTemplateSelector构造中的绑定表达式引起的-因为IsLoggedIn
和EditMode
不是DependencyProperties。
So the question now is: how can I have a DataTemplateSelector that depends on the status of the ViewModel if I cannot bind to values? 所以现在的问题是:如果我不能绑定到值,我怎么能有一个依赖ViewModel状态的DataTemplateSelector?
Whilst you could use a DataTemplateSelector
or something of that ilk, it probably won't surprise you to find that in Caliburn.Micro has this functionality built-in in the form of View.Context
and the ViewLocator
虽然您可以使用DataTemplateSelector
或类似的东西,但在Caliburn中发现它可能不会让您感到惊讶View.Context
具有以View.Context
和ViewLocator
的形式内置的此功能。
On your VM you can create a property which provides a context string which CM will use to resolve the View - since it uses naming conventions, you just need to provide the correct namespace/name for the sub-view along with a context string for it to locate an alternative view 在您的VM上,您可以创建一个属性,该属性提供CM将用于解析视图的上下文字符串-因为它使用命名约定,所以您只需要为子视图提供正确的名称空间/名称以及上下文字符串即可查找替代视图
In your VM you can create a context property that uses the user details to determine its value: 在您的VM中,您可以创建一个上下文属性,该上下文属性使用用户详细信息来确定其值:
ie 即
public class SomeViewModel
{
public string Context
{
get
{
if (IsEditMode && _r.Name == IsLoggedInAs) return "Current";
else return "Other";
}
}
// ... snip other code
}
The only problem I see (one that probably has a workaround) is that you want to determine the view from inside a ViewModel
- usually you determine the context higher up and pass that to a ContentControl
and CM uses it when locating the view for that VM 我看到的唯一问题(一个可能具有解决方法的问题)是您想从ViewModel
内部确定视图-通常,您确定更高的上下文并将其传递给ContentControl
,CM在为该VM查找视图时使用它
eg 例如
your main VM: 您的主要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
}
and associated view 和相关视图
<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>
then your VM naming structure would be: 那么您的VM命名结构将是:
- ViewModels
|
----- SomeSubViewModel.cs
|
- SomeSubView.xaml
|
- SomeSubView
|
----- Alternative.xaml
and CM would know to look in the SomeSubView
namespace for a control called Alternative
based on the original VM name and the Context
property (SomeSubViewModel minus Model plus dot plus Context
which is SomeSubView.Alternative) 并且CM会知道要根据原始VM名称和Context
属性在SomeSubView
命名空间中查找名为Alternative
的控件(SomeSubViewModel减去Model加上点加Context
,即SomeSubView.Alternative)
So I'd have to have a play around as this is the standard way of doing it. 所以我必须去玩,因为这是标准的做法。 If you were to do it this way you'd have to either create a sub viewmodel and add a ContentControl
to your view and bind the View.Context
property to the Context
property on the VM, or add the Context
property higher up (to the parent VM). 如果您采用这种方式,则必须创建一个子视图模型,然后将ContentControl
添加到视图中,然后将View.Context
属性绑定到VM上的Context
属性,或者将Context
属性添加到更高的位置(父VM)。
I'll look at some alternatives - if there is no way to get the current ViewModel to decide its view based on a property using standard CM, you could customise the ViewLocator
and maybe use an interface (IProvideContext or somesuch) which provides the ViewLocator
with a context immediately -(I don't think you can't hook directly into the view resolution process from a VM) 我将研究一些替代方法-如果无法使用标准CM使当前ViewModel根据属性确定视图,则可以自定义ViewLocator
,也可以使用提供ViewLocator
的接口(IProvideContext或诸如此类)立即提供上下文-(我认为您不能直接从VM挂接到视图解析过程)
I'll come back with another answer or an alternative shortly! 我很快会再提供另一个答案或替代方法!
EDIT: 编辑:
Ok this seems to be the most straightforward way to do it. 好的,这似乎是最简单的方法。 I just created an interface which provides Context
directly from a VM 我刚刚创建了一个直接从VM提供Context
的接口
public interface IProvideContext
{
string Context { get; }
}
Then I customised the ViewLocator
implementation (you can do this in Bootstrapper.Configure()
) to use this if no context was already specified: 然后,我自定义了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);
};
This should work for you and allows you to set the context directly on the target ViewModel
- obviously this will probably only work for a View-First approach 这应该适合您,并允许您直接在目标ViewModel
上设置上下文-显然,这可能仅适用于“视图优先”方法
So all you need to do is structure your views as I showed above (the correct namespaces etc) then set the Context
property on your VM based on the value of IsLoggedInAs
and EditMode
因此,您需要做的就是构造如上所示的视图(正确的名称空间等),然后根据IsLoggedInAs
和EditMode
的值在VM上设置Context
属性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.