Reputation: 3897
I made this Minimal, Complete, and Verifiable example of the challenge I'm facing. It works but is definitely not MVVM and is nothing more than a hack.
It updates the status bar based on some property changing in the user control. It also updates when the user changes the tab. For the real deal, it will be displaying a records count (how many data rows are displayed in each tab).
There must be a cleaner way of doing this...
Note: In my actual implementation, the data context for each user control is different. So if someone has a suggestion for binding that involves a similar data context, please take that into consideration.
<Window x:Class="TabControlStatusBarBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControlStatusBarBinding"
Title="MainWindow" Height="150" Width="300"
x:Name="Window">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TabControl Grid.Row="0">
<TabItem Header="Tab1">
<local:UserControl1 x:Name="UC1_A"/>
</TabItem>
<TabItem Header="Tab2">
<local:UserControl1 x:Name="UC1_B" />
</TabItem>
<TabItem Header="Tab3">
<local:UserControl1 x:Name="UC1_C"/>
</TabItem>
</TabControl>
<StatusBar Grid.Row="1">
<TextBlock Text="{Binding DP_StatusBarText, ElementName=Window, FallbackValue='No Updates'}"/>
</StatusBar>
</Grid>
</Window>
<UserControl x:Name="MyUserControl" x:Class="TabControlStatusBarBinding.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="50" d:DesignWidth="90" GotFocus="MyUserControl_GotFocus">
<Grid Background="Gray">
<TextBox x:Name="UC1" Text="test" HorizontalAlignment="Center" VerticalAlignment="Center" TextChanged="UC1_TextChanged"/>
</Grid>
</UserControl>
namespace TabControlStatusBarBinding
{
public partial class MainWindow : Window
{
public static DependencyProperty dp_StatusBarText = DependencyProperty.Register("DP_StatusBarText", typeof(string), typeof(MainWindow));
public string DP_StatusBarText
{
get { return (string)GetValue(dp_StatusBarText); }
set { SetValue(dp_StatusBarText, value); }
}
public MainWindow()
{
InitializeComponent();
DP_StatusBarText = "Main window loaded";
UC1_A.StatusUpdated += MyEventHandlerFunction_StatusUpdated;
UC1_B.StatusUpdated += MyEventHandlerFunction_StatusUpdated;
UC1_C.StatusUpdated += MyEventHandlerFunction_StatusUpdated;
}
public void MyEventHandlerFunction_StatusUpdated(object sender, EventArgs e)
{
DP_StatusBarText = (string)sender;
}
}
}
namespace TabControlStatusBarBinding
{
public partial class UserControl1 : UserControl
{
public event EventHandler StatusUpdated;
public UserControl1()
{
InitializeComponent();
}
private void RaiseStatusUpdatedEvent(string SendText)
{
if (this.StatusUpdated != null)
this.StatusUpdated(SendText, new EventArgs());
}
private void UC1_TextChanged(object sender, TextChangedEventArgs e)
{
RaiseStatusUpdatedEvent(UC1.Text);
}
private void MyUserControl_GotFocus(object sender, RoutedEventArgs e)
{
RaiseStatusUpdatedEvent(UC1.Text);
}
}
}
Upvotes: 2
Views: 419
Reputation: 101483
Well your example only uses views - no models, no viewmodels, so not very clear what are your problems with MVVM :) But I'll try to help you introducing some data in your example.
First, just simple data item.
public class TestDataItem : INotifyPropertyChanged {
public int ID { get; set; }
private string _text;
public string Text
{
get { return _text; }
set
{
if (value == _text) return;
_text = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
UserControl1. Just binds textbox Text to model Text property. Codebehind is empty.
<UserControl x:Class="WpfApplication2.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="Gray">
<TextBox Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</UserControl>
Window. Tabs in tab control are now bound to a list of data items. Tab content is just UserControl1 (it will inherit data context).
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wpfApplication2="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="500">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TabControl x:Name="tabControl" Grid.Row="0" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}">
<TabControl.ItemTemplate>
<DataTemplate DataType="wpfApplication2:TestDataItem">
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="wpfApplication2:TestDataItem">
<wpfApplication2:UserControl1 />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<StatusBar Grid.Row="1">
<TextBlock Text="{Binding SelectedItem.Text, FallbackValue='No Updates'}"/>
</StatusBar>
</Grid>
</Window>
Window code-behind:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
View model:
public class MainViewModel : INotifyPropertyChanged {
public IEnumerable<TestDataItem> Items => new[] {
new TestDataItem() {ID = 100, Text = "item1"},
new TestDataItem() {ID = 200, Text = "item2"},
new TestDataItem() {ID = 300, Text = "item3"}
};
private string _statusText = "No data selected";
public string StatusText
{
get { return _statusText; }
set
{
if (value == _statusText) return;
_statusText = value;
OnPropertyChanged();
}
}
private TestDataItem _selectedItem;
public TestDataItem SelectedItem
{
get { return _selectedItem; }
set
{
if (Equals(value, _selectedItem)) return;
_selectedItem = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
So we just bind status to SelectedItem.Text and done.
Upvotes: 1