1

私のViewmodelには、LoggedInAsstringEditMode型とbool型のプロパティがあります。ReaderList次のような表示目的でItemsControlにバインドするListプロパティもあります。

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

Caliburn.Microを使用しているので、名前付けによってバインドが自動的に行われます。アプリケーションがEditModeであり、Personがログインしている場合は、根本的に異なる表示が必要なため、DataTemplateSelectorを使用したいと思います。これが私のリソースの宣言です、

<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の構築におけるバインディング式が原因でした-IsLoggedInEditModeはDependencyPropertiesではないためです。

したがって、ここでの問題は、値にバインドできない場合に、ViewModelのステータスに依存するDataTemplateSelectorをどのように作成できるかということです。

4

1 に答える 1

1

そのようなものを使用することもできますDataTemplateSelectorが、Caliburn.Microにこの機能が組み込まれているのは、おそらく驚くことではありませんView.ContextViewLocator

VMで、CMがビューを解決するために使用するコンテキスト文字列を提供するプロパティを作成できます-命名規則を使用するため、サブビューの正しい名前空間/名前とそのコンテキスト文字列を指定する必要があります別のビューを見つけるには

VMで、ユーザーの詳細を使用してその値を決定するコンテキストプロパティを作成できます。

すなわち

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

    // ... snip other code
}

私が見る唯一の問題(おそらく回避策があるもの)は、内部からビューを決定したいということですViewModel-通常、コンテキストをより高い位置で決定し、それをaに渡しContentControl、CMはその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
}

および関連するビュー

<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名とプロパティ(SomeSubViewModelからモデルとドットプラス(SomeSubView.Alternative )を差し引いたもの)に基づいてSomeSubView呼び出されたコントロールの名前空間を検索することを認識します。AlternativeContextContext

それで、これがそれをする標準的な方法であるので、私は遊んでいなければならないでしょう。この方法で行う場合は、サブビューモデルを作成してビューにを追加し、プロパティをVMのプロパティにContentControlバインドするか、プロパティを上位(親VM)に追加する必要があります。View.ContextContextContext

いくつかの代替案を見ていきます-現在のViewModelに、標準のCMを使用してプロパティに基づいてビューを決定させる方法がない場合は、をカスタマイズして、コンテキストViewLocatorを提供するインターフェイス(IProvideContextなど)を使用できます。ViewLocatorすぐに-(VMからビュー解決プロセスに直接フックすることはできないと思います)

私はすぐに別の答えまたは代替案で戻ってきます!

編集:

わかりました、これはそれを行うための最も簡単な方法のようです。ContextVMから直接提供するインターフェイスを作成しました

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-明らかにこれはおそらくビューファーストアプローチでのみ機能します

したがって、必要なのは、上記で示したようにビューを構造化して(正しい名前空間など)、とContextの値に基づいてVMのプロパティを設定することだけです。IsLoggedInAsEditMode

于 2013-03-13T11:08:00.057 に答える