Adding initialized ViewModels to ObservableCollection displaying null property values - c#

When adding ViewModels to an ObservableCollection, which is shown on the MainWindow as an ItemsControl with the ObservableCollection as the ItemsSource. The initial values of the View are displayed as null. I know this because on debugging and changing the value of the TextBox I see that the Name field is set to null, but when I press the button to add new ViewModels it is setting the Name field but then not displaying the name. This app has been condensed for ease of debugging. So it seems that while the ObservableCollection is communicating to the view it is not receiving the proper values somehow.
MainWindow
<Window x:Class="LifeCalculator.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:myControl="clr-namespace:LifeCalculator.Views" xmlns:views="clr-namespace:LifeFinanceCalculator.Views"
Title="{Binding Title}" Height="350" Width="525">
<Grid>
<Button Margin="47,24,373,269" Content="Add ViewModels" Command="{Binding AddCommandItem}"/>
<ScrollViewer Margin="28,75,37,72">
<ItemsControl ItemsSource="{Binding ListExampleItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:exampleView/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
MainWindowViewModel
using Prism.Mvvm;
using System.Collections.ObjectModel;
using LifeFinanceCalculator.ViewModels;
using System.Windows.Input;
using Prism.Commands;
namespace LifeCalculator.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private ObservableCollection<exampleViewModel> _listExampleItems;
public ObservableCollection<exampleViewModel> ListExampleItems
{
get => _listExampleItems;
set
{
SetProperty(ref _listExampleItems, value);
}
}
public ICommand AddCommandItem { get; set; }
public MainWindowViewModel()
{
_listExampleItems = new ObservableCollection<exampleViewModel>();
AddCommandItem = new DelegateCommand(ListItem);
}
private void ListItem()
{
_listExampleItems.Add(new exampleViewModel() { Name = "Chris" });
_listExampleItems.Add(new exampleViewModel() { Name = "Olivia" });
}
}
}
exampleView
<UserControl x:Class="LifeFinanceCalculator.Views.exampleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<TextBox Text="{Binding Name}" FontSize="22"/>
</Grid>
</UserControl>
exampleViewModel
using Prism.Mvvm;
namespace LifeFinanceCalculator.ViewModels
{
public class exampleViewModel : BindableBase
{
private string _name;
public string Name
{
get => _name;
set
{
SetProperty(ref _name, value);
}
}
public exampleViewModel()
{
}
}
}

The ViewModelLocator used by Prism was the issue. The ViewModelLocator re-intializes the View with a new ViewModel. To solve the issue :
prism:ViewModelLocator.AutoWireViewModel = "False"

Related

WinUI 3 Binding from a DataTemplate to the parent ViewModel

In a WinUI 3 application, using CommunityToolkit.Mvvm, I have XXXPage which has a ListDetailsView.
I defined a DateTemplate for the ListDetailsView DetailsTemplate, which contains a user control : XXXDetailControl.
I am trying to bind the InstallClicked event of the XXXDetailControl to the page's ViewModel InstallCommand, with no success.
<DataTemplate x:Key="DetailsTemplate">
<Grid>
<views:XXXDetailControl
DetailMenuItem="{Binding}"
InstallClicked="{ ???? }" />
</Grid>
...
</DataTemplate>
How can I setup this binding so that the event from the control defined in the DataTemplate is binded to the page viewmodel command ? How can I setup this binding so that the selected item is sent with the event ?
XXXPage.xaml :
<Page
x:Class="XXXPage"
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:models="using:XXX.Models"
xmlns:views="using:XXX.Views"
xmlns:behaviors="using:XXX.Behaviors"
xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:viewmodels="using:XXX.ViewModels"
behaviors:NavigationViewHeaderBehavior.HeaderMode="Never"
mc:Ignorable="d">
<Page.Resources>
...
<DataTemplate x:Key="DetailsTemplate">
<Grid>
<views:XXXDetailControl
DetailMenuItem="{Binding}"
InstallClicked="{Binding ViewModel.InstallCommand, ElementName=?}" CommandParameter="{x:Bind (viewmodels:XXXDetailViewModel)}" />
</Grid>
...
</DataTemplate>
</Page.Resources>
<Grid x:Name="ContentArea">
...
<controls:ListDetailsView
x:Uid="ListDetails"
x:Name="ListDetailsViewControl"
DetailsTemplate="{StaticResource DetailsTemplate}"
ItemsSource="{x:Bind ViewModel.Items}"/>
</Grid>
</Page>
XXXPage.cs :
public sealed partial class XXXPage: Page
{
public XXXViewModel ViewModel
{
get;
}
public XXXPage()
{
ViewModel = App.GetService<XXXViewModel >();
InitializeComponent();
}
}
the XXXViewModel :
public class XXXViewModel : ObservableRecipient, INavigationAware
{
private XXXDetailViewModel? _selected;
public XXXDetailViewModel? Selected
{
get => _selected;
set
{
SetProperty(ref _selected, value);
}
}
public ObservableCollection<XXXDetailViewModel> Items { get; private set; } = new ObservableCollection<XXXDetailViewModel>();
public ICommand InstallCommand;
}
If you can use Command and CommandParameter inside your user control, you can do it this way.
DetailsControl.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Windows.Input;
namespace CustomControlEvent;
public sealed partial class DetailControl : UserControl
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
nameof(Text),
typeof(string),
typeof(DetailControl),
new PropertyMetadata(default));
public static readonly DependencyProperty ClickCommandProperty = DependencyProperty.Register(
nameof(ClickCommand),
typeof(ICommand),
typeof(DetailControl),
new PropertyMetadata(default));
public static readonly DependencyProperty ClickCommandParameterProperty = DependencyProperty.Register(
nameof(ClickCommandParameter),
typeof(object),
typeof(DetailControl),
new PropertyMetadata(default));
public DetailControl()
{
this.InitializeComponent();
}
public object ClickCommandParameter
{
get => (object)GetValue(ClickCommandParameterProperty);
set => SetValue(ClickCommandParameterProperty, value);
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public ICommand ClickCommand
{
get => (ICommand)GetValue(ClickCommandProperty);
set => SetValue(ClickCommandProperty, value);
}
}
DetailsControl.xaml
<UserControl
x:Class="CustomControlEvent.DetailControl"
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:local="using:CustomControlEvent"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Button
Command="{x:Bind ClickCommand, Mode=OneWay}"
CommandParameter="{x:Bind ClickCommandParameter, Mode=OneWay}"
Content="{x:Bind Text, Mode=OneWay}" />
</Grid>
</UserControl>
MainPageViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace CustomControlEvent;
public partial class MainPageViewModel : ObservableObject
{
[RelayCommand]
private void Run(object commandParameter)
{
}
[ObservableProperty]
private ObservableCollection<DetailsViewModel> items = new()
{
new DetailsViewModel() { Details = "A" },
new DetailsViewModel() { Details = "B" },
new DetailsViewModel() { Details = "C" },
};
}
MainPage.xaml.cs
using Microsoft.UI.Xaml.Controls;
namespace CustomControlEvent;
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public MainPageViewModel ViewModel { get; } = new();
}
MainPage.xaml
This page is named ThisPage in order to bind from the DataTemplate.
<Page
x:Class="CustomControlEvent.MainPage"
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:local="using:CustomControlEvent"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="ThisPage"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<StackPanel>
<StackPanel.Resources>
<DataTemplate
x:Key="DetailsTemplate"
x:DataType="local:DetailsViewModel">
<Grid>
<local:DetailControl
ClickCommand="{Binding ElementName=ThisPage, Path=ViewModel.RunCommand}"
ClickCommandParameter="{x:Bind}"
Text="{x:Bind Details, Mode=OneWay}" />
</Grid>
</DataTemplate>
</StackPanel.Resources>
<ListView
ItemTemplate="{StaticResource DetailsTemplate}"
ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}" />
</StackPanel>
</Page>
Did you try to give the Page a Name like this?:
<Page ...
Name="thePage">
<Page.Resources>
...
<DataTemplate x:Key="DetailsTemplate">
<Grid>
<views:XXXDetailControl
DetailMenuItem="{Binding}"
InstallClicked="{Binding ViewModel.InstallCommand, ElementName=thePage}" ... />
</Grid>
...
</DataTemplate>
</Page.Resources>
This seems to work for a ListView.

WinUI 3 - How to open a control in a page upon click of button from a User Control c#?

I have an ItemsRepeater on the page's XAML code where it's ItemsSource property is bind to a list of User Control (ObersvableCollection), a custom control I made. In this User Control there's a button that I wish would open a SplitView pane that I set in the Page's Xaml code. I'm thinking I need to get an instance of the page in the User Control's code behind, on the click event, but I have no idea how.
You can do it this way.
TestUserControl.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Windows.Input;
namespace WinUI3App1;
public sealed partial class TestUserControl : UserControl
{
public static readonly DependencyProperty ClickCommandProperty = DependencyProperty.Register(
nameof(ClickCommand),
typeof(ICommand),
typeof(TestUserControl),
new PropertyMetadata(null));
public TestUserControl()
{
InitializeComponent();
}
public ICommand ClickCommand
{
get => (ICommand)GetValue(ClickCommandProperty);
set => SetValue(ClickCommandProperty, value);
}
}
TestUseControl.xaml
<UserControl
x:Class="WinUI3App1.TestUserControl"
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"
mc:Ignorable="d"
x:Name="ThisControl">
<StackPanel Orientation="Horizontal">
<Button Command="{x:Bind ClickCommand}" CommandParameter="{Binding ElementName=ThisControl}" Content="Click" />
</StackPanel>
</UserControl>
MainWindow.xaml.cs
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace WinUI3App1;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Install the CommunityToolkit.Mvvm NuGet package
// to avoid implementing commands yourself.
ClickCommand = new RelayCommand<TestUserControl>(OnClick);
for (int i = 0; i < 10; i++)
{
TestUserControls.Add(new TestUserControl()
{
ClickCommand = ClickCommand
});
}
}
public ObservableCollection<TestUserControl> TestUserControls { get; set; } = new();
public ICommand ClickCommand { get; set; }
private void OnClick(TestUserControl? sender)
{
SplitViewControl.IsPaneOpen = true;
}
}
MainWindow.xaml
<Window
x:Class="WinUI3App1.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"
mc:Ignorable="d">
<SplitView x:Name="SplitViewControl">
<SplitView.Pane>
<Grid/>
</SplitView.Pane>
<StackPanel Orientation="Vertical">
<ItemsRepeater ItemsSource="{x:Bind TestUserControls}" />
</StackPanel>
</SplitView>
</Window>

WPF Usercontrol Bindings with MVVM ViewModel not working

I've spent some time trying to solve this problem but couldn't find a solution.
I am trying to bind commands and data inside an user control to my view model. The user control is located inside a window for navigation purposes.
For simplicity I don't want to work with Code-Behind (unless it is unavoidable) and pass all events of the buttons via the ViewModel directly to the controller. Therefore code-behind is unchanged everywhere.
The problem is that any binding I do in the UserControl is ignored.
So the corresponding controller method is never called for the command binding and the data is not displayed in the view for the data binding. And this although the DataContext is set in the controllers.
Interestingly, if I make the view a Window instead of a UserControl and call it initially, everything works.
Does anyone have an idea what the problem could be?
Window.xaml (shortened)
<Window x:Class="Client.Views.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:local="clr-namespace:Client.Views"
mc:Ignorable="d">
<Window.Resources>
<local:SubmoduleSelector x:Key="TemplateSelector" />
</Window.Resources>
<Grid>
<StackPanel>
<Button Command="{Binding OpenUserControlCommand}"/>
</StackPanel>
<ContentControl Content="{Binding ActiveViewModel}" ContentTemplateSelector="{StaticResource TemplateSelector}">
<ContentControl.Resources>
<DataTemplate x:Key="userControlTemplate">
<local:UserControl />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Grid>
</Window>
MainWindowViewModel (shortened)
namespace Client.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
private ViewModelBase mActiveViewModel;
public ICommand OpenUserControlCommand { get; set; }
public ViewModelBase ActiveViewModel
{
get { return mActiveViewModel; }
set
{
if (mActiveViewModel == value)
return;
mActiveViewModel = value;
OnPropertyChanged("ActiveViewModel");
}
}
}
}
MainWindowController (shortened)
namespace Client.Controllers
{
public class MainWindowController
{
private readonly MainWindow mView;
private readonly MainWindowViewModel mViewModel;
public MainWindowController(MainWindowViewModel mViewModel, MainWindow mView)
{
this.mViewModel = mViewModel;
this.mView = mView;
this.mView.DataContext = mViewModel;
this.mViewModel.OpenUserControlCommand = new RelayCommand(ExecuteOpenUserControlCommand);
}
private void OpenUserControlCommand(object obj)
{
var userControlController = Container.Resolve<UserControlController>(); // Get Controller instance with dependency injection
mViewModel.ActiveViewModel = userControlController.Initialize();
}
}
}
UserControlSub.xaml (shortened)
<UserControl x:Class="Client.Views.UserControlSub"
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:local="clr-namespace:Client.Views"
xmlns:viewModels="clr-namespace:Client.ViewModels"
mc:Ignorable="d">
<Grid>
<ListBox ItemsSource="{Binding Models}" SelectedItem="{Binding SelectedModel}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Attr}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel>
<Button Command="{Binding Add}">Kategorie hinzufügen</Button>
</StackPanel>
</Grid>
</UserControl>
UserControlViewModel (shortened)
namespace Client.ViewModels
{
public class UserControlViewModel : ViewModelBase
{
private Data _selectedModel;
public ObservableCollection<Data> Models { get; set; } = new ObservableCollection<Data>();
public Data SelectedModel
{
get => _selectedModel;
set
{
if (value == _selectedModel) return;
_selectedModel= value;
OnPropertyChanged("SelectedModel");
}
}
public ICommand Add { get; set; }
}
}
UserControlController (shortened)
namespace Client.Controllers
{
public class UserControlController
{
private readonly UserControlSub mView;
private readonly UserControlViewModel mViewModel;
public UserControlController(UserControlViewModel mViewModel, UserControlSub mView)
{
this.mViewModel = mViewModel;
this.mView = mView;
this.mView.DataContext = mViewModel;
this.mViewModel.Add = new RelayCommand(ExecuteAddCommand);
}
private void ExecuteAddCommand(object obj)
{
Console.WriteLine("This code gets never called!");
}
public override ViewModelBase Initialize()
{
foreach (var mod in server.GetAll())
{
mViewModel.Models.Add(mod);
}
return mViewModel;
}
}
}

Specify Binding of DependencyProperty in UserControl itself

Following up on my previous question (Change brushes based on ViewModel property)
In my UserControl I have have a DependencyObject. I want to bind that object to a property of my ViewModel. In this case a CarViewModel, property name is Status and returns an enum value.
public partial class CarView : UserControl
{
public CarStatus Status
{
get { return (CarStatus)GetValue(CarStatusProperty); }
set { SetValue(CarStatusProperty, value); }
}
public static readonly DependencyProperty CarStatusProperty =
DependencyProperty.Register("Status", typeof(CarStatus), typeof(CarView), new PropertyMetadata(OnStatusChanged));
private static void OnStatusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var control = (CarView)obj;
control.LoadThemeResources((CarStatus)e.NewValue == CarStatus.Sold);
}
public void LoadThemeResources(bool isSold)
{
// change some brushes
}
}
<UserControl x:Class="MySolution.Views.CarView"
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"
xmlns:views="clr-MySolution.Views"
mc:Ignorable="d"
views:CarView.Status="{Binding Status}">
<UserControl.Resources>
</UserControl.Resources>
<Grid>
<TextBlock Text="{Binding Brand}"FontSize="22" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<UserControl
Where do I need to specify this binding? In the root of the UserControl it gives an error:
The attachable property 'Status' was not found in type 'CarView'
In my MainWindow I bind the CarView using a ContentControl:
<ContentControl
Content="{Binding CurrentCar}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewmodel:CarViewModel}">
<views:CarView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
My ViewModel:
[ImplementPropertyChanged]
public class CarViewModel
{
public Car Car { get; private set; }
public CarStatus Status
{
get
{
if (_sold) return CarStatus.Sold;
return CarStatus.NotSold;
}
}
}
your binding isn't well written. instead of writing views:CarView.Status="{Binding Status}" you should write only Status="{Binding Status}"
It seems that your Control is binding to itself.
Status is looked for in CarView.
You should have a line of code in your control CodeBehind like :
this.DataContext = new ViewModelObjectWithStatusPropertyToBindFrom();
Regards

Passing parameter via Binding to UserConrol DependencyProperty

I have custom user control with the only property - SubHeader.
<UserControl x:Class="ExpensesManager.TopSection"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<StackPanel>
<Label Name="Header" Content="Constant header text" Style="{StaticResource Header}"/>
<Label Name="SubHeader" Content="{Binding SubHeaderText}" Style="{StaticResource SubHeader}"/>
</StackPanel>
public partial class TopSection : UserControl
{
public TopSection()
{
this.InitializeComponent();
}
public static readonly DependencyProperty SubHeaderTextProperty =
DependencyProperty.Register("SubHeaderText", typeof(string), typeof(TopSection));
public string SubHeaderText
{
get { return (string)GetValue(SubHeaderTextProperty); }
set { SetValue(SubHeaderTextProperty, value); }
}
}
There are two usages in xaml. First with the constant text:
...
<my:TopSection SubHeaderText="Constant text"/>
...
Another one using binding:
<Page x:Class="MyNamespace.MyPage"
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"
xmlns:my="clr-namespace:My"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
...
<my:TopSection SubHeaderText="{Binding MyModel.SubHeaderText}"/>
...
</Page>
My page code behind:
public partial class MyPage : Page
{
private MyModel myModel;
public MyModel MyModel
{
get
{
return this.myModel?? (this.myModel = new MyModel());
}
}
public MyPage(MyEntity entity)
{
this.InitializeComponent();
this.MyModel.MyEntity = entity;
}
}
MyModel code:
public class MyModel : NotificationObject
{
private MyEntity myEntity;
private string subHeaderText;
public MyEntity MyEntity
{
get
{
return this.myEntity;
}
set
{
if (this.myEntity!= value)
{
this.myEntity= value;
this.RaisePropertyChanged(() => this.MyEntity);
this.RaisePropertyChanged(() => this.SubHeaderText);
}
}
}
public string SubHeaderText
{
get
{
return string.Format("Name is {0}.", this.myEntity.Name);
}
}
}
The problem is that second one doesn't work. If I pass the constant text - it is displayed, if I use binding to the other property - nothing is displayed.
Does anybody knows what's wrong with the code? Thanks.
The problem is you set DataContext on the UserControl element. It will cause the following binding
<my:TopSection SubHeaderText="{Binding MyModel.SubHeaderText}"/>
to be relative to that DataContext, which is UserControl itself - so it cannot find the value.
To fix this, I suggest you not set DataContext on the UserControl, but the StackPanel inside:
<UserControl x:Class="ExpensesManager.TopSection"
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">
<StackPanel DataContext="{Binding RelativeSource={RelativeSource AncesterType=UserControl}}">
<Label Name="Header" Content="Constant header text" Style="{StaticResource Header}"/>
<Label Name="SubHeader" Content="{Binding SubHeaderText}" Style="{StaticResource SubHeader}"/>
</StackPanel>
Many people set DataContext on UserControl but that's really BAD. When you use the UserControl later, you have no idea the DataContext is actually set internally and will not respect the outside DataContext - really confusing. This rule also applies to other properties.
MyModel is a property in your DataContext? Try to check what object is your DataContext. If your data context is an object of your class MyModel you doesn't need the MyModel. part in your binding.
This kind of bindings always are to objects in your data context.
Hope this tips helps.
Declare your UserControl like this:
<my:TopSection
x:Name="myControl">
Then change your binding to this:
<my:TopSection SubHeaderText="{Binding MyModel.SubHeaderText, ElementName=myControl}"/>
You didn't set the Model in your UserControl
public partial class TopSection : UserControl
{
public class SampleViewModel { get; set; }
public TopSection()
{
this.InitializeComponent();
this.DataContext = new SampleViewModel();
}
public static readonly DependencyProperty SubHeaderTextProperty =
DependencyProperty.Register("SubHeaderText", typeof(string), typeof(TopSection));
public string SubHeaderText
{
get { return (string)GetValue(SubHeaderTextProperty); }
set { SetValue(SubHeaderTextProperty, value); }
}
}
Update
Since you don't want Model to known to the View. Create a ViewModel
public class SampleViewModel : NotificationObject
{
public class MyModel { get; set; }
public class SampleViewModel()
{
MyModel = new MyModel() { SubHeaderText = "Sample" };
RaisePropertyChange("MyModel");
}
}

Categories