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?
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:
- 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.
- 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
- 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;
}
}
}