1

私はmvvmライトツールキットベースのプロジェクトに取り組んでいます。対応するViewModelsをMainView含むとがあります。DetailsView両方のVMがに登録されていNotificationMessageます。

// MainViewModel.cs and DetailsViewModel.cs
private void RegisterMessages()
{
    Messenger.Default.Register<NotificationMessage>(this, NotificationMessageHandler);
}

「ShowDetails」メッセージを受信するMainViewModelと、「DetailsView」を作成するサービスを呼び出します

// MainViewModel.cs
private void NotificationMessageHandler(NotificationMessage msg)
{
    if (msg.Notification == "ShowDetails")
    {
        _detailsService.ShowDetails(); // Does something like (new DetailsView).ShowDialog()
    }
}

DetailsViewを使用しViewModelLocatorて、既存のDetailsViewModalものをDataContextとして取得します。

DetailsViewModel内部状態を更新したり、データを要求したりするために、「ShowDetails」メッセージを受信する必要があります。

// DetailsViewModel.cs
private void NotificationMessageHandler(NotificationMessage msg)
    {
        if (msg.Notification == "ShowDetails")
        {
            UpdateViewModel();
        }
    }

DetailsViewここで問題があります。モーダルウィンドウにしたいので、それを呼び出しShowDialog()ます。DetailsViewこれは、が再び閉じられるまでメッセンジャーをブロックしているようです。したがって、DetailsViewModalモーダルウィンドウが閉じられた後にメッセージを受信します。これを修正するための解決策はありますか?

DetailsViewModal前に登録できればうまくいくと思いますMainViewModel。これにより、MessageHandler呼び出しの順序が変更され、VMの更新がブロックの前に発生しShowDialog()ます。しかし、MainViewModelそれが何であるかという理由で、最初に作成されて登録されます。は初めて必要になったときにDetailsViewModel作成されるため、常にレースに負けます。ViewModalLocator

4

1 に答える 1

5

残念ながら、私はあなたの特定の問題を再現することができませんでした。MainWindowViewLoadedイベントハンドラーで別のスレッドを起動しました。特定のメッセージを継続的に送信するだけのスレッド。次に、SecondWindowViewでShowDialog()を呼び出しました。このビューモデルは、この特定のメッセージをリッスンするように登録されています。2番目のウィンドウのビューモデルのメッセージハンドラーが繰り返し実行されました。実際、私のビューモデルはアプリケーションの起動時にViewModelLocatorによってすでに作成されているため、ShowDailog()が呼び出される前でもハンドラーが呼び出されていました。あなたのケースで何が起こっているのかをよりよく理解するために、もう少しコードを見る必要があります(つまり、詳細ウィンドウを作成しているサービス、または問題を再現するためにコンパイルできるもの)。

代わりに、子ウィンドウに対して次のアプローチを試すことができます。アプリケーションのどこかに次のクラスを定義します。

public class ShowChildWindowMessage : MessageBase { }
public class HideChildWindowMessage : MessageBase { }
public class DisplayDetailsMessage : MessageBase { }

次に、次のChildWindowVMクラスを作成し、MainWindowVMが初期化されるのと同じ方法でViewModelLocatorで初期化します。

public class ChildWindowVM : ViewModelBase
{
    private ViewModelBase m_currentContent;
    public ViewModelBase CurrentContent
    {
        get { return m_currentContent; }
        set
        {
            NotifySetProperty(ref m_currentContent, value, () => CurrentContent);
            if (m_currentContent != null)
            {
                m_currentContent.Refresh();
                Messenger.Default.Send(new ShowChildWindowMessage());
            }
        }
    }

    public ChildWindowVM()
    {
        Messenger.Default.Register<DisplayDetailsMessage>(this, OnDisplayDetails);
    }

    private void OnDisplayDetails(DisplayDetailsMessage msg)
    {
        CurrentContent = ViewModelLocator.DetailsViewModel; // or whatever view model you want to display
    }
}

Refresh()メソッドはDetailsViewModelクラスで定義され、ウィンドウを表示する前に実行する初期化を処理します。CurrentContentプロパティが設定されると、メッセージがMainWindowViewに送信され、コンテンツを表示するChildWindowViewインスタンスが作成されることに注意してください。

MainWindowViewコードは次のようになります。

public partial class MainWindowView : Window
{
    private ChildWindowView m_childWindowView;

    public MainWindowView()
    {
        InitializeComponent();
        Closing += () => ViewModelLocator.CleanUp();      

        Messenger.Default.Register<ShowChildWindowMessage>(this, OnShowChildWindow);
        Messenger.Default.Register<HideChildWindowMessage>(this, OnHideChildWindow);
    }

    private void OnShowChildWindow(ShowChildWindowMessage msg)
    {
        m_childWindowView = new ChildWindowView();
        m_childWindowView.ShowDialog();
    }

    private void OnHideChildWindow(HideChildWindowMessage msg)
    {
        m_childWindowView.Close();
    }
}

最後のステップは、CurrentContentプロパティをChildWindowVMクラスからChildWindowViewクラスにバインドすることです。これは、ChildWindowViewのxamlで行われます。

<Window x:Class="Garmin.Cartography.AdminBucketTools.ChildWindowView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding Path=ChildWindowVm, Source={StaticResource Locator}}">

<Grid>
    <ContentPresenter Content="{Binding Path=CurrentContent}" />
</Grid>

これで、電話をかけるだけで、アプリケーションのどこからでも詳細を表示できます。

Messenger.Default.Send(new DisplayDetailsMessage());

そして、あなたは呼び出すことによってプログラムでウィンドウを閉じることができます

Messenger.Default.Send(new HideChildWindowMessage());

また、MessageBaseから必要な数のクラスを派生させ、ChildWindowVMクラスに登録することもできます。次に、各メッセージハンドラーで、CurrentContentプロパティを適切なビューモデルに設定するだけで、表示するコンテンツを指定できます。

もう1つ、実際に。子ウィンドウで役立つものを実際に表示したい場合は、ビューとビューモデル間のテンプレートバインディングを指定する必要があります。これは、アプリケーションリソースのxamlを介して実行できます。

<DataTemplate DataType="{x:Type viewmodels:DetailsViewModel}">
    <views:DetailsView />
</DataTemplate>

名前空間(つまり、「viewmodels」と「views」)を定義することを忘れないでください。

于 2012-09-19T15:19:02.490 に答える