prism:如何将 shell.xaml 中的内容绑定到区域内容变量?

发布于 2024-10-07 20:55:12 字数 1005 浏览 2 评论 0原文

应用程序位于 prism/mvvm/mef 中,并使用像 StockTraderRI 这样的属性加载。

我的 shell 窗口包含一个 StatusBar 的 DockPanel,它对于 shell 来说是全局的,而不是每个区域本地的。

看起来像这样:

<DockPanel>
  <StatusBar DockPanel.Dock="Bottom">
    <StatusBarItem>
      <TextBlock Text="{Binding Source={x:Static bcl:Configuration.Global}, Path=LoggedOn.User}"/>
    </StatusBarItem>
    <StatusBarItem>
      <TextBlock Text="{Binding StateMessage}"/>
    </StatusBarItem>
  </StatusBar>
  <ContentControl x:Name="MainContent" cal:RegionManager.RegionName="MainRegion"/>
</DockPanel>

绑定到全局变量确实效果很好。现在我想将 StatusBarItem 中的 StateMessage 绑定到加载到 MainRegion 中的任何控件中的属性 StateMessage。
我的第一个猜测是使用类似的东西:

<TextBlock Text="{Binding Path=DataContext.StateMessage,Source={StaticResource MainContent}}"/>

但这当然行不通,因为 MainContent 不是 StaticResource。

任何人都可以向我指出一种将 Text 属性绑定到加载到 MainRegion 中的 UserControl 的某些属性的方法吗?

App is in prism/mvvm/mef and uses loading by attribute like StockTraderRI.

My shell window contains a DockPanel for a StatusBar, which is global to the shell, not local to each region.

Looks something like this:

<DockPanel>
  <StatusBar DockPanel.Dock="Bottom">
    <StatusBarItem>
      <TextBlock Text="{Binding Source={x:Static bcl:Configuration.Global}, Path=LoggedOn.User}"/>
    </StatusBarItem>
    <StatusBarItem>
      <TextBlock Text="{Binding StateMessage}"/>
    </StatusBarItem>
  </StatusBar>
  <ContentControl x:Name="MainContent" cal:RegionManager.RegionName="MainRegion"/>
</DockPanel>

Binding to a global variable does work well. Now I'd like to bind the StateMessage in the StatusBarItem to the property StateMessage in whatever control is loaded into the MainRegion.
My first guess was to use something like:

<TextBlock Text="{Binding Path=DataContext.StateMessage,Source={StaticResource MainContent}}"/>

But this of course does not work, since MainContent is no StaticResource.

Can anyone point me to a way to bind the Text property to some property of the UserControl loaded into the MainRegion?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

无人问我粥可暖 2024-10-14 20:55:12

我认为您无法找到一种干净的方法来直接绑定到区域中对象的属性。实现此目的的一种方法是让指定区域中的所有视图继承一个公共接口,以便它们都实现相同的属性(例如,SystemName)。您可以绑定到包含该属性的路径,并使用 ValueConverter 将类型从 System.Object 更改为接口类型。

我遇到的问题是,它限制了您可以放入该区域的内容,并且似乎将 shell 与其中显示的类型结合得比我个人想要的更多。我建议采用不同的路线:由于状态栏是 shell 的一部分,因此为 shell 提供一个 ViewModel,其中包含状态栏绑定到的属性,并使用事件来跟踪状态值何时更改。这样做有一些好处:

  1. 您的事件可以发送任何具有 shell 可以绑定到的任意数量属性的对象。这使得状态栏以外的其他东西也可以利用它。
  2. 不与 shell 中的各种对象交互的视图不必传输事件。这使得在该区域中显示的较大组件的子视图不会与 shell 产生不利的交互,或者实现它们不需要的任何接口。
  3. 这是 Prism 框架中定义良好的机制,因此是分发信息的预期方式

请注意,您显然可以将事件定义为您需要的任何内容 - 我只是放入一个对象类型的参数,但您可能希望它更具体地满足您的整体需求。

// Use normal binding for Shell -> ShellViewModel
public sealed ShellViewModel : INotifyPropertyChanged
{
   private readonly IEventAggregator = eventAggregator;

   public ShellViewModel(IEventAggregator eventAggregator)
   {
      _eventAggregator = eventAggregator;

      _eventAggregator.GetEvent<MainRegionViewLoaded>.Subscribe(
         new Action<Object>(HandleMainRegionViewLoaded));
   }

   private String _statusName;
   public String StatusName
   {
      get { return _statusName; }
      set
      {
         if (_statusName != value)
         {
            _statusName = value;
            RaisePropertyChanged("StatusName"); // Not showing this...
         }
      }
   }

   private void HandleMainRegionViewLoaded(Object param)
   {
      // Not doing all the checking here, such as null, etc.
      var statusName = param as String;
      StatusName = statusName;
   }
}

现在,您的视图所要做的就是实现 IActiveAware,并在它们变为活动状态时引发事件。或者,您可以让视图的 ViewModel 执行此操作(这可能更好,但需要一些额外的通信。有一些解释 这里 这样做。)。

public void MainView : UserControl, IActiveAware
{

   private readonly IEventAggregator _eventAggregator;

   public MainView(IEventAggregator eventAggregator)
   {
      InitializeComponent();

      _eventAggregator = eventAggregator;
   }

   private bool _isActive;
   public bool IsActive
   {
      get { return _isActive; }
      set
      {
         if (value)
         {
            _eventAggregator.GetEvent<MainRegionViewLoaded>.Publish(new MainRegionViewLoaded("My Name"));
         }
         _isActive = value;
      }
   }
}

I don't think you're going to be able to find a clean way to bind directly to a property in an object in a region. One way you might be able to do this would be to have all Views in the specified region inherit from a common interface, such that they all implement the same Property (say, SystemName). You may be able to bind to a path that includes that property, and use a ValueConverter to change the type from System.Object to the interface type.

The problem I have with that is that it limits what you can put into the region, and seems to couple the shell with the types displayed in it more than I'd personally like. I'd suggest a different route: since your status bar is part of your shell, have a ViewModel for the shell with your property that the status bar binds to, and that uses an event to track when the status value changes. A few benefits from doing this:

  1. Your event can send any object with any number of properties that your shell can bind to. This lets things other than your status bar also utilize it.
  2. Views that don't interact with the various objects in your shell don't have to transmit the event. This lets subviews of a larger component that get displayed in the region not adversely interact with the shell, or implement any interfaces that they don't need to
  3. It is a well defined mechanism in the Prism framework, and hence an expected way to distribute information

Note that you could obviously define the event to be whatever you need - I just put in a parameter of type Object, but you'd probably want that to be more specific to your overall needs.

// Use normal binding for Shell -> ShellViewModel
public sealed ShellViewModel : INotifyPropertyChanged
{
   private readonly IEventAggregator = eventAggregator;

   public ShellViewModel(IEventAggregator eventAggregator)
   {
      _eventAggregator = eventAggregator;

      _eventAggregator.GetEvent<MainRegionViewLoaded>.Subscribe(
         new Action<Object>(HandleMainRegionViewLoaded));
   }

   private String _statusName;
   public String StatusName
   {
      get { return _statusName; }
      set
      {
         if (_statusName != value)
         {
            _statusName = value;
            RaisePropertyChanged("StatusName"); // Not showing this...
         }
      }
   }

   private void HandleMainRegionViewLoaded(Object param)
   {
      // Not doing all the checking here, such as null, etc.
      var statusName = param as String;
      StatusName = statusName;
   }
}

Now, all your views have to do is implement IActiveAware, and raise the event when they become Active. Alternatively, you could have the ViewModel's for your View do this (which is probably better, but would require some additional communication. There's some explanation here on doing that.).

public void MainView : UserControl, IActiveAware
{

   private readonly IEventAggregator _eventAggregator;

   public MainView(IEventAggregator eventAggregator)
   {
      InitializeComponent();

      _eventAggregator = eventAggregator;
   }

   private bool _isActive;
   public bool IsActive
   {
      get { return _isActive; }
      set
      {
         if (value)
         {
            _eventAggregator.GetEvent<MainRegionViewLoaded>.Publish(new MainRegionViewLoaded("My Name"));
         }
         _isActive = value;
      }
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文