简体   繁体   English

如何使此DataTemplateSelector工作?

[英]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构造中的绑定表达式引起的-因为IsLoggedInEditMode不是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.ContextViewLocator的形式内置的此功能。

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 因此,您需要做的就是构造如上所示的视图(正确的名称空间等),然后根据IsLoggedInAsEditMode的值在VM上设置Context属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使网格在DataTemplateSelector中可见 - How make a grid visible in a DataTemplateSelector DataTemplateSelector /绑定不起作用 - DataTemplateSelector / Binding does not work 如何在TreeViewItem的模板上使用DataTemplateSelector? - How to use DataTemplateSelector on TreeViewItem's template? 如何强制重新选择DataTemplateSelector - How to force re-selection of DataTemplateSelector 属性更改时如何触发 DataTemplateSelector? - How to trigger DataTemplateSelector when property changes? 如何对齐 DataTemplateSelector 选择的 DataTemplate 中的文本? - How to align Text in DataTemplate choosen by a DataTemplateSelector? 提供一个DataTemplateSelector运行时资源,如何? WPF / XAML - Supplying a DataTemplateSelector runtime resources, how? WPF/XAML WinRT:如何在ContentControl的DataTemplateSelector中访问父项的ItemsSource - WinRT: How to access parent's ItemsSource in DataTemplateSelector of ContentControl 如何在WPF中的DataTemplateSelector类中查找UserControl中的资源? - How to find a resource in a UserControl from a DataTemplateSelector class in WPF? 如何在 C# 中为自定义 DataTemplateSelector 获取 DataTemplate 的 {x:DataType} - How to get {x:DataType} for a DataTemplate in C# for custom DataTemplateSelector
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM