Bind visibility property to a variable - c#

I have a Border with Label inside a Window,
<Border x:Name="Border1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="21" Margin="229,164,0,0" VerticalAlignment="Top" Width="90" Opacity="0.5">
<Grid>
<Label Content="test"/>
</Grid>
</Border>
I have also a Variable:
public bool vis = false;
How could I bind the vis variable with border Visibility property?

You don't need to make any converter.
Add a binding to a Visibility property for the border:
<Border x:Name="Border1" Visibility="{Binding Visibility}" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="21" Margin="229,164,0,0" VerticalAlignment="Top" Width="90" Opacity="0.5">
<Grid>
<Label Content="test"/>
</Grid>
</Border>
And then create the Visibility property in your ViewModel:
private Visibility visibility;
public Visibility Visibility
{
get
{
return visibility;
}
set
{
visibility = value;
OnPropertyChanged("Visibility");
}
}
Now you can set Visible or Hidden to your Visibility property as follows:
Visibility = Visibility.Visible;
// or
Visibility = Visibility.Hidden;
The Visibility enum is located in System.Windows namespace, so your ViewModel has to include using System.Windows;.

If you already have your bool variable in a viewmodel, you have two things to do:
make it a property, like:
public bool vis { get; set; }
And you need a visibility converter for your property then:
It is described here:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/3c0bef93-9daf-462f-b5da-b830cdee23d9
The example assumes that you have a viewmodel and use Binding
Here is some demo code that I made from your snippet:
ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace StackOverflowWpf2
{
public class BorderViewModel : INotifyPropertyChanged
{
private bool borderVisible = false;
public bool BorderVisible
{
get
{
return borderVisible;
}
set
{
borderVisible = value;
NotifyPropertyChanged("BorderVisible");
}
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
XAML:
<Window x:Class="StackOverflowWpf2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Window.Resources>
<Grid>
<Border x:Name="Border1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="21" Margin="229,164,0,0" VerticalAlignment="Top" Width="90" Opacity="0.5"
Visibility="{Binding Path=BorderVisible, Converter={StaticResource BoolToVisConverter} }" >
<Grid>
<Label Content="test"/>
</Grid>
</Border>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="381,35,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click"
/>
</Grid>
</Window>
Some Codebehind quick testcode: (actually is MainWindow.xaml.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace StackOverflowWpf2
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public BorderViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new BorderViewModel();
this.DataContext = ViewModel;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
var vis = (this.DataContext as BorderViewModel).BorderVisible;
(this.DataContext as BorderViewModel).BorderVisible = !vis;
}
}
}

You can't bind field. You can only bind public properties or dependency properties.
Using public property (you have to implement INotifyPropertyChanged interface to have property->binding):
public partial class MainWindow : Window, INotifyPropertyChanged
{
private bool vis;
public bool Vis
{
get { return vis; }
set
{
if (vis != value)
{
vis = value;
OnPropertyChanged("Vis"); // To notify when the property is changed
}
}
}
public MainWindow()
{
InitializeComponent();
Vis = true;
// DataContext explains WPF in which object WPF has to check the binding path. Here Vis is in "this" then:
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Vis = !Vis; // Test Code
}
#region INotifyPropertyChanged implementation
// Basically, the UI thread subscribes to this event and update the binding if the received Property Name correspond to the Binding Path element
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
The XAML code is:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="MainWindow2" Height="233" Width="392">
<Window.Resources>
<!-- You can implement your own BooleanToVisibilityConverter but there is one already implemented. So the next line creates an instance of the BooleanToVisibilityConverter that you will be able to reference with the specified key -->
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Button Content="Click on the button to test" Click="Button_Click" Margin="0,0,0,165" />
<Border x:Name="Border1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="21" Margin="229,164,0,0" VerticalAlignment="Top" Width="90" Opacity="0.5"
Visibility="{Binding Vis, Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- The previous line define the binding : the path = Vis and the Converter to use -->
<Grid>
<Label Content="test"/>
</Grid>
</Border>
</Grid>
</Window>

First you will need to make vis a Property:
private bool _vis;
public bool Vis
{
get{return _vis;}
set
{
if(_vis != value)
{
_vis = value;
}
}
}
Then you will need a ValueConverter.
[ValueConversion(typeof(bool), typeof(Visibility))]
public class VisibilityConverter : IValueConverter
{
public const string Invert = "Invert";
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(Visibility))
throw new InvalidOperationException("The target must be a Visibility.");
bool? bValue = (bool?)value;
if (parameter != null && parameter as string == Invert)
bValue = !bValue;
return bValue.HasValue && bValue.Value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
You will need to create an instance of the converter like so in your resources:
<UserControl.Resources>
<cvt:VisibilityConverter x:Key="VisibilityConverter" />
</UserControl.Resources>
Then you can bind your border like so:
<Border x:Name="Border1" Visibility="{Binding vis, Converter={StaticResource VisibilityConverter}}>
<Grid>
<Label Content="test"/>
</Grid>
</Border>

Another solution is to use Trigger Style:
<Border x:Name="Border1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="21" Margin="229,164,0,0" VerticalAlignment="Top" Width="90" Opacity="0.5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=vis, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }" Value="False">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Label Content="test"/>
</Grid>
</Border>
In the model class:
public class ModelClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private bool _vis;
public bool vis
{
get => _vis;
set
{
_vis = value;
NotifyPropertyChanged("vis");
}
}
}
Don't forget to bind DataContext with your model !
DataContext = new ModelClass();

Using INotifyPropertyChanged and #Ladislav Ondris's example. Along with the following binding Visibility="{x:Bind Visibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
worked for me. Wasn't showing the update otherwise using Visibility.Collapsed and Visbility.Visible.

Related

How to open a specific tabitem on restart of WPF c# app on button click?

I have multiple tabitem's (tab1, tab2, ... etc) in a WPF tabcontrol. When I click a button I want to restart the app and open a specific tabitem, say tab2 (in MVVM friendly way).
To restart the app I use
Process.Start(Application.ResourceAssembly.Location);
Application.Current.Shutdown();
But how do I specify which tabitem to display after restart?
Obiously, your new app instance needs to "know something" about the requirement to open a specific tab item.
There are other possiblities like creating a configuration file, but probably a command line parameter will serve well here. To start the new instance you may use something like Process.Start(Application.ResourceAssembly.Location, "/StartTab=MyTab3");
Then, in your ViewModel, have a string property like public string SelectedTabName {get; set;} and initialize that during the construction of the VM:
var tabInfo = Environment.GetCommandLineArgs().FirstOrDefault(a => a.ToLower().StartsWith("/starttab="));
if (!string.IsNullOrWhiteSpace(tabInfo))
{
SelectedTabName = tabInfo.Substring(tabInfo.IndexOf('=')+1);
}
Finally, in the XAML code, bind the IsSelected property of your tab items to the SelectedTabName string, with the help of a StringToBooleanConverter, using the ConverterParameter like the name of the tab.
public class StringMatchesParameterToBooleanConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
if (value is not string val)
return false;
if (parameter is not string param)
return false;
return val == param;
}
[ExcludeFromCodeCoverage]
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
{
throw new NotImplementedException();
}
}
Your TabControl Xaml code could work like so
<TabControl>
<TabControl.Resources>
<converters:StringMatchesParameterToBooleanConverter x:Key="StringMatchesParameterToBooleanConverter" />
</TabControl.Resources>
<TabItem x:Name="MyTab1"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab1}">
</TabItem>
<TabItem x:Name="MyTab2"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab2}">
</TabItem>
<TabItem x:Name="MyTab3"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab3}">
</TabItem>
</TabControl>
Meanwhile I have done a test project (in VS 2022 using .net 6.0) and it works mostly as described earlier. No problem with throwing an exception in the ConvertBack part of the converter. Only difference required was restarting the program using Environment.ProcessPath instead of Application.ResourceAssembly.Location:
MainWindow.xaml:
<Window x:Class="TestStartWithButtonClick.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:TestStartWithButtonClick"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=True}"
SizeToContent="WidthAndHeight"
Title="MainWindow" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TabControl Grid.Row="0" Grid.ColumnSpan="3">
<TabControl.Resources>
<local:StringMatchesParameterToBooleanConverter x:Key="StringMatchesParameterToBooleanConverter" />
</TabControl.Resources>
<TabItem x:Name="MyTab1" Header="MyTab1"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab1}">
<StackPanel Width="300" Height="100" />
</TabItem>
<TabItem x:Name="MyTab2" Header="MyTab2"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab2}">
<StackPanel Width="300" Height="100" />
</TabItem>
<TabItem x:Name="MyTab3" Header="MyTab3"
IsSelected="{Binding SelectedTabName, Converter={StaticResource StringMatchesParameterToBooleanConverter}, ConverterParameter=MyTab3}">
<StackPanel Width="300" Height="100" />
</TabItem>
</TabControl>
<Button Grid.Row="1" Grid.Column="0" Margin="0,0,6,0" Content="Start With Tab 1" Command="{Binding StartNewInstanceCommand}" CommandParameter="MyTab1"/>
<Button Grid.Row="1" Grid.Column="1" Margin="6,0,6,0" Content="Start With Tab 2" Command="{Binding StartNewInstanceCommand}" CommandParameter="MyTab2"/>
<Button Grid.Row="1" Grid.Column="2" Margin="6,0,0,0" Content="Start With Tab 3" Command="{Binding StartNewInstanceCommand}" CommandParameter="MyTab3"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace TestStartWithButtonClick
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
}
MainViewModel.cs:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
namespace TestStartWithButtonClick
{
public class MainViewModel : INotifyPropertyChanged
{
private string _selectedTabName = string.Empty;
public event PropertyChangedEventHandler? PropertyChanged;
public MainViewModel()
{
var tabInfo = Environment.GetCommandLineArgs().FirstOrDefault(a => a.ToLower().StartsWith("/starttab="));
if (!string.IsNullOrWhiteSpace(tabInfo))
{
SelectedTabName = tabInfo.Substring(tabInfo.IndexOf('=') + 1);
}
else
{
SelectedTabName = "MyTab1";
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public StartNewInstanceCommand StartNewInstanceCommand { get; set; } = new();
public string SelectedTabName
{
get => _selectedTabName;
set
{
if (value == _selectedTabName) return;
_selectedTabName = value;
OnPropertyChanged(nameof(SelectedTabName));
}
}
}
public class StartNewInstanceCommand : ICommand
{
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
if (parameter is not string tabItem)
throw new ArgumentException("parameter is not string", nameof(parameter));
Process.Start(Environment.ProcessPath, $"/StartTab={tabItem}");
Application.Current.Shutdown(0);
}
public event EventHandler? CanExecuteChanged;
}
}
StringMatchesParameterToBooleanConverter.cs:
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Windows.Data;
namespace TestStartWithButtonClick
{
public class StringMatchesParameterToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not string val)
return false;
if (parameter is not string param)
return false;
return val == param;
}
[ExcludeFromCodeCoverage]
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

DataContext binding in UserControl WPF

Trying to get DataContext in UserControl.
My
structure
I have the model Car
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace AutoShop.MVVM.Model
{
class Car : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
private string _Model;
public string Model
{
get
{
return _Model;
}
set
{
_Model = value;
OnPropertyChanged();
}
}
private string _Mark;
public string Mark
{
get
{
return _Mark;
}
set
{
_Mark = value;
OnPropertyChanged();
}
}
private float _Volume;
public float Volume
{
get
{
return _Volume;
}
set
{
_Volume = value;
OnPropertyChanged();
}
}
private int _DateOfIssue;
public int DateOfIssue
{
get
{
return _DateOfIssue;
}
set
{
_DateOfIssue = value;
OnPropertyChanged();
}
}
public enum EngineTypes {
DISEL,
PETROL
};
private EngineTypes _EngineType;
public EngineTypes EngineType
{
get
{
return _EngineType;
}
set
{
_EngineType = value;
OnPropertyChanged();
}
}
private string _ImageUrl;
public string ImageUrl
{
get
{
return _ImageUrl;
}
set
{
_ImageUrl = value;
OnPropertyChanged();
}
}
public Car()
{
}
}
}
And I have main view model
using AutoShop.MVVM.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace AutoShop.MVVM.ViewModel
{
class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
public HomeViewModel HomeVM;
private object _CurrentPage;
public object CurrentPage
{
get
{
return _CurrentPage;
}
set
{
_CurrentPage = value;
OnPropertyChanged();
}
}
private List<Car> _Cars;
public List<Car> Cars
{
get
{
return _Cars;
}
set
{
_Cars = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
Cars = new List<Car>() {
new Car
{
Mark = "Audi",
Model = "asdf",
Volume = 1.4F,
DateOfIssue = 2019,
EngineType = Car.EngineTypes.DISEL,
ImageUrl = "img/img"
},
new Car
{
Mark = "Moto",
Model = "asdf",
Volume = 1.4F,
DateOfIssue = 2019,
EngineType = Car.EngineTypes.DISEL,
ImageUrl = "img/img"
},
new Car
{
Mark = "Some",
Model = "asdf",
Volume = 1.4F,
DateOfIssue = 2019,
EngineType = Car.EngineTypes.DISEL,
ImageUrl = "img/img"
}
};
HomeVM = new HomeViewModel();
CurrentPage = HomeVM;
}
}
}
I need to display cars on the ProductPage.xaml and I do it by the next way
<UserControl x:Class="AutoShop.MVVM.View.ProductPage"
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:local="clr-namespace:AutoShop.MVVM.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
>
<StackPanel Background="#fff">
<WrapPanel>
<Grid Width="200px" Margin="30 0 0 0">
<TextBox x:Name="field4" Tag="C:\Users\user\Desktop\Learning\Весенний семестр\ООТП\AutoShop\AutoShop\Images\u.png" Style="{StaticResource TextBoxTemplate}" />
<TextBlock IsHitTestVisible="False" Text="Марка" Padding="20 10 0 0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="FontFamily" Value="/Fonts/#Montserrat Light" />
<Setter Property="Foreground" Value="#ccc" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=field4}" Value="">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
<Grid Width="200px" Margin="30 0 0 0">
<TextBox x:Name="field7" Tag="C:\Users\user\Desktop\Learning\Весенний семестр\ООТП\AutoShop\AutoShop\Images\u.png" Style="{StaticResource TextBoxTemplate}" />
<TextBlock IsHitTestVisible="False" Text="username" Padding="20 10 0 0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="FontFamily" Value="/Fonts/#Montserrat Light" />
<Setter Property="Foreground" Value="#ccc" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=field7}" Value="">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
<Grid Width="200px" Margin="30 0 0 0">
<ComboBox Style="{StaticResource ComboBoxTheme}" SelectedIndex="0">
<ComboBoxItem>
<TextBlock Text="asdasdasd" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="fsdfsd" />
</ComboBoxItem>
</ComboBox>
</Grid>
</WrapPanel>
<Grid Height="400">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl x:Name="ItemsWrapper" ItemsSource="{Binding Cars}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="10" BorderBrush="Black" BorderThickness="2" Height="279">
<Grid Height="279" Width="200">
<Image Source="{Binding Path=Image}" Width="100" Height="100" VerticalAlignment="Top" Margin="0 10 0 0" />
<TextBlock Text="{Binding Path=Name, StringFormat='Name: {0}'}" VerticalAlignment="Top" Margin="0,120,0,0"/>
<TextBlock Text="{Binding Path=Mark, StringFormat='Rating: {0}' }" VerticalAlignment="Top" Margin="0 180 0 0" />
<TextBlock Text="{Binding Path=Model, StringFormat='Category: {0}'}" VerticalAlignment="Top" Margin="0,200,0,0" />
<TextBlock Text="{Binding Path=Volume, StringFormat='Price: {0}'}" VerticalAlignment="Top" Margin="0,160,0,0" />
<TextBlock Text="{Binding Path=DateOfIssue, StringFormat='Description: {0}'}" VerticalAlignment="Top" Margin="0,140,0,0" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</StackPanel>
</UserControl>
And it the MainForm.xaml I added ProductPage.xaml
<ContentControl Margin="10" Grid.Column="1" Content="{Binding CurrentPage}"/>
The problem is that nothing is being outputted, I think it might be due to the loss of context. How do I properly pass the context to UserControl (ProductPage.xaml)?
ProductPage.xaml
Update:
I set DataContext for MainWindow
And DataContext working becouse {Binding CurrentPage} is working, but binding on Cars field is not working
Add this to the Window.Resources. Of course DataContext must be set for the MainForm.
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type xmlnsForVewModel:MainViewModel}">
<ProductPage/>
</DataTemplate>
</Window.Resources>
</Window>
and if for ProductPage HomeViewModel supposed to be a DataContext, then HomeViewModel has to have Cars property, not MainViewModel.
1.find the MainForm.xaml.cs
2.find ctor
public MainForm()
{
InitializeComponent();
//set the MainForm DataContext
DataContext = new MainViewModel();
}
3.you should add ContentControl.DataTemplate to show your HomeViewModel
----20210427 update
Here demo i make, project name is WpfApp4
MainWindow.xaml
<Window x:Class="WpfApp4.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:WpfApp4"
xmlns:views="clr-namespace:WpfApp4.Views"
xmlns:viewModels="clr-namespace:WpfApp4.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<viewModels:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding ItemsSource, Mode=OneWay}" SelectedItem="{Binding SelectedCarViewModel, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Column="1">
<ContentControl Content="{Binding SelectedCarViewModel}">
<ContentControl.ContentTemplate>
<!--Style your product page here-->
<DataTemplate DataType="{x:Type viewModels:CarViewModel}">
<UniformGrid Columns="2">
<TextBlock Text="Name"/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="Volume"/>
<TextBlock Text="{Binding Volume}"/>
<TextBlock Text="Price"/>
<TextBlock Text="{Binding Price}"/>
</UniformGrid>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace WpfApp4
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindowViewModel.cs
using System.Collections.ObjectModel;
namespace WpfApp4.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
public ObservableCollection<CarViewModel> ItemsSource { get; } = new ObservableCollection<CarViewModel>();
private CarViewModel selectedCarViewModel;
public CarViewModel SelectedCarViewModel
{
get { return this.selectedCarViewModel; }
set { SetProperty(ref this.selectedCarViewModel, value); }
}
public MainWindowViewModel()
{
ItemsSource.Add(new CarViewModel() { Name = "BMW", Volume = 10, Price = 100 });
ItemsSource.Add(new CarViewModel() { Name = "Toyota", Volume = 5, Price = 80 });
ItemsSource.Add(new CarViewModel() { Name = "Benz", Volume = 20, Price = 150 });
// don't let view binding null value, so here initialze property "SelectedCarViewModel"
this.selectedCarViewModel = ItemsSource[0];
}
}
}
CarViewModel.cs
namespace WpfApp4.ViewModels
{
class CarViewModel : ViewModelBase
{
private string name;
public string Name
{
get { return this.name; }
set { SetProperty(ref this.name, value); }
}
private float volume;
public float Volume
{
get { return this.volume; }
set { SetProperty(ref this.volume, value); }
}
private decimal price;
public decimal Price
{
get { return this.price; }
set { SetProperty(ref this.price, value); }
}
}
}
ViewModelBase.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
namespace WpfApp4.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T t, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(t, value))
{
return false;
}
else
{
t = value;
RaisePropertyChanged(propertyName);
return true;
}
}
}
}

Edits in wpf window don't update Observable object

Why do edits in my child window not affect the collection which I'm directly modifying from my main view models data context? When i click properties...it opens a seconds window where users can change the names of the people in the collection. However when i reopen the properties window Doug's name will be set back to default. I plan on making it possible for users to add/remove people from within this window as well. I'm a bit perplexed on why it wouldn't directly change and affect the bound data.
Main Window
Child Window
MainWindowViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfApplication1
{
public class MainWindowViewModel : NotifyBase
{
private ObservableCollection<Person> crowd;
public ObservableCollection<Person> Crowd
{
get { return crowd ?? (crowd = new ObservableCollection<Person>()); }
set { Set(ref crowd, value); }
}
private ICommand mainWindowCommand;
public ICommand MainWindowCommand
{
get
{
return mainWindowCommand ?? (mainWindowCommand = new RelayCommand<CommandTargets>(ExecuteCommand));
}
}
private void ExecuteCommand(CommandTargets parameter)
{
switch (parameter)
{
case CommandTargets.EditProperties:
PropertiesView PropertiesWindow = new PropertiesView();
PropertiesWindow.Show();
break;
}
}
public MainWindowViewModel()
{
Crowd.Add(new Person("Doug"));
}
}
public enum CommandTargets
{
EditProperties,
}
public class Person
{
public string Name { get; set; }
public Person( string name)
{
this.Name = name;
}
}
class RelayCommand<T> : ICommand
{
private readonly Func<T, bool> _canExecuteHandler;
private readonly Action<T> _executeHandler;
public RelayCommand(Action<T> executeHandler)
: this(executeHandler, null)
{ }
public RelayCommand(Action<T> executeHandler, Func<T, bool> canExecuteHandler)
{
_canExecuteHandler = canExecuteHandler;
_executeHandler = executeHandler;
}
public bool CanExecute(object parameter)
{
return _canExecuteHandler != null ? _canExecuteHandler((T)parameter) : true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_executeHandler((T)parameter);
}
public void RaiseCanExecuteChanged()
{
EventHandler handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
public abstract class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
RaisePropertyChanged(propertyName);
return true;
}
}
}
MainWindow.xaml
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="250" Width="250"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Menu DockPanel.Dock="Top">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding MainWindowCommand}"/>
</Style>
</Menu.Resources>
<MenuItem Header="_File">
<MenuItem Header="_Properties..." CommandParameter="{x:Static local:CommandTargets.EditProperties}"/>
</MenuItem>
</Menu>
</Grid>
</Window>
PropertiesView.xaml
<Window x:Class="WpfApplication1.PropertiesView"
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:WpfApplication1"
mc:Ignorable="d"
Title="PropertiesView" Height="300" Width="300"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView Grid.Row="0"
x:Name="ItemList"
ItemsSource="{Binding Crowd, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalContentAlignment="Stretch"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="100" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
<StackPanel Grid.Row="1" DataContext="{Binding ElementName=ItemList, Path=SelectedItem}">
<Label Grid.Column="0" Grid.Row="0" Content="Name: " VerticalAlignment="Center" Foreground="Black"/>
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
Margin="5"/>
</StackPanel>
</Grid>
</Window>
You are using different instances of the view model. You'll need to share the same instance for the editing to work.
private void ExecuteCommand(CommandTargets parameter)
{
switch (parameter)
{
case CommandTargets.EditProperties:
PropertiesView PropertiesWindow = new PropertiesView();
PropertiesWindow.DataContext=this; //<----------- add this line
PropertiesWindow.Show();
break;
}
}
This is happening because each PropertiesView is using its own instance of the MainWindowViewModel so the two Views are referring to different ObservableCollections
I think the affect you are going for can be seen by making the private crowd member variable static.
private static ObservableCollection<Person> crowd;

Data-binding to KVP of Object,Object not allowing properties to be used or updated in real time?

I have Operation class and Result class that I use to build a list, I use xaml to bind to the properties and a converter to return some content based on certain properties, Most of this works....
To make it easy I will post the xaml then note what works and what I need help with.
<UserControl x:Class="OperationListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:telerik="http://schemas.telerik.com/2008/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"
xmlns:prism="http://www.codeplex.com/prism"
xmlns:inf="clr-namespace:***;assembly=***"
xmlns:c="clr-namespace:****.***"
d:DesignHeight="300" d:DesignWidth="300">
<Control.Resources>
<c:LanguageTextConverter x:Key="langConverter" />
<c:ResultViewConverter x:Key="statusConverter" />
<c:OpDetailViewConverter x:Key="opConverter" />
</Control.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<telerik:RadGridView Name="OperationGrid"
Grid.Column="0"
RowHeight="75"
SelectionMode="Single"
SelectedItem="{Binding SelectedOperation, Mode=TwoWay}"
ItemsSource="{Binding Operations}"
IsReadOnly="True"
AutoGenerateColumns="False"
RowDetailsVisibilityMode="VisibleWhenSelected"
ShowGroupPanel="False"
RowIndicatorVisibility="Collapsed" >
<telerik:RadGridView.RowDetailsTemplate>
<DataTemplate>
<ContentControl Grid.Column="1" Template="{Binding Converter={StaticResource opConverter}}" />
</DataTemplate>
</telerik:RadGridView.RowDetailsTemplate>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="Name"
DataMemberBinding="{Binding Key.operationName, Converter={StaticResource langConverter}}"
Width="2*"
IsGroupable="False" />
<telerik:GridViewDataColumn Header="Result"
Width="1*"
MaxWidth="75">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<Grid>
<ContentControl Template="{Binding Value, Converter={StaticResource statusConverter}}"/>
</Grid>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</Grid>
everything up to the final binding works, and even the final binding works to a point...
<ContentControl Template="{Binding Value, Converter={StaticResource statusConverter}}"/>
this works, when binding to Value the converter fires on load and loads the appropriate xaml.
However no additional changes update this value...
In the code behind at some point the Value.status is updated , the setter on the status property fires an propertyChanged. but nothing is caught on the front end.
public OverallStatus status
{
get { return this.Status; }
set { this.Status = value; onPropertyChanged(this, "status"); }
}
What is the property syntax to bind to a Value.Property (currently doesn't work at all) AND have it recognize the propertyChanged setter 3 levels down from the view.
I have created a sample showing nested properties. When I change a property value at button click, it is reflected in the grid. More can be read here :
Property path syntax
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="524.627" Width="862.313">
<StackPanel>
<DataGrid x:Name="Dgrd" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Value.Status.Name}" ClipboardContentBinding="{x:Null}" Header="Status"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Button" HorizontalAlignment="Left" Margin="29,38,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</StackPanel>
</Window>
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Dgrd.ItemsSource = DataStore.Operations;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DataStore.Operations[0].Value.Status.Name = "N/A"; // change first item
}
}
public class DataStore
{
//static List<Operation> _operations;
public static List<Operation> Operations;
static DataStore()
{
Operations =
new List<Operation>() {
new Operation(){ Value = new RecordInner(){ Status = new OverallStatus (){ Name="Pending"}}},
new Operation(){ Value = new RecordInner(){ Status = new OverallStatus (){ Name="Started"}}},
new Operation(){ Value = new RecordInner(){ Status = new OverallStatus (){ Name="Completed"}}}
};
}
}
public class Operation
{
RecordInner _value;
public RecordInner Value
{
get { return _value; }
set { _value = value; }
}
}
public class RecordInner
{
OverallStatus _status;
public OverallStatus Status
{
get { return _status; }
set { _status = value; }
}
}
public class OverallStatus:INotifyPropertyChanged
{
string _name;
public string Name { get { return _name; } set { _name = value; OnPropertyChanged("name"); } }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}

Data Binding Not Working Properly

In this program, I'm able to add one tab at a time by clicking the "Add Course" button. Ideally, the header of the tab should be the course name I entered and the text in the textbox , which is on the tab, should display the course name.
However, it's not functioning correctly. When I tried to add more than 1 tabs, each time it gives me this error message:
System.Windows.Data Error: 40 : BindingExpression path error: 'Text' property not found on 'object' ''MyHomeworkViewModel' (HashCode=33010577)'. BindingExpression:Path=Text; DataItem='MyHomeworkViewModel' (HashCode=33010577); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')
Also, it seems to "override" other tab's text (just text, not header). For example, if I add a tab with header "a", the text of that is also "a". Then if I add "B", both textboxes on two tabs become "B". However, if I print out the Text property of each tab (MyHomeworkModel in this case), they are "a" and "B", respectively.
I have been debugging this whole day but no luck. Any help would be appreciated!
My View (DataContext set to MyHomeworkViewModel):
<Window x:Class="MyHomework__MVVM_.MyHomeworkView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="My Homework" Height="450" Width="800" ResizeMode="CanMinimize">
<Grid Margin="0,0,10,10">
<TabControl HorizontalAlignment="Left" Height="330" VerticalAlignment="Top" Width="764" Margin="10,10,0,0" ItemsSource="{Binding AllTabs}" SelectedItem="{Binding SelectedTab}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Content">
<Setter.Value>
<Grid>
<TextBox Text="{Binding Text}" FontSize="16" AcceptsReturn="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</TextBox>
</Grid>
</Setter.Value>
</Setter>
<Setter Property="FontSize" Value="20"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
<Button Content="Add Course" HorizontalAlignment="Left" VerticalAlignment="Top" Width="105" Margin="10,351,0,0" Height="50" Command="{Binding AddCourseCommand}"/>
<Button Content="Drop Course" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Margin="138,379,0,0" Height="22" Command="{Binding DropCourseCommand, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Save HW" HorizontalAlignment="Left" VerticalAlignment="Top" Width="105" Margin="669,351,0,0" Height="50"/>
</Grid>
</Window>
My Model:
using System.ComponentModel;
namespace MyHomework__MVVM_
{
class MyHomeworkModel : INotifyPropertyChanged
{
private string header, text;
public event PropertyChangedEventHandler PropertyChanged;
public string Header
{
get
{
return header;
}
set
{
header = value;
OnPropertyChanged("Header");
}
}
public string Text
{
get
{
return text;
}
set
{
text = value;
OnPropertyChanged("Text");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
My ViewModel:
using MyHomework;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
namespace MyHomework__MVVM_
{
class MyHomeworkViewModel : INotifyPropertyChanged
{
private ObservableCollection<MyHomeworkModel> allTabs;
private MyHomeworkModel selectedTab;
private MyHomeworkView mainWindow;
public event PropertyChangedEventHandler PropertyChanged;
public MyHomeworkViewModel(MyHomeworkView mainWindow)
{
allTabs = new ObservableCollection<MyHomeworkModel>();
this.mainWindow = mainWindow;
AddCourseCommand = new AddCourseCommand(this);
DropCourseCommand = new DropCourseCommand(this);
}
public ObservableCollection<MyHomeworkModel> AllTabs
{
get
{
return allTabs;
}
set
{
allTabs = value;
OnPropertyChanged("AllTabs");
}
}
public MyHomeworkModel SelectedTab
{
get
{
return selectedTab;
}
set
{
selectedTab = value;
OnPropertyChanged("SelectedTab");
}
}
public ICommand AddCourseCommand
{
get;
private set;
}
public ICommand DropCourseCommand
{
get;
private set;
}
public void AddNewTab()
{
NewCourseName ncn = new NewCourseName();
ncn.Owner = mainWindow;
ncn.ShowDialog();
if (ncn.courseName != null)
{
MyHomeworkModel newTab = new MyHomeworkModel();
newTab.Header = ncn.courseName;
newTab.Text = ncn.courseName;
AllTabs.Add(newTab);
SelectedTab = newTab;
}
foreach (MyHomeworkModel item in AllTabs)
{
Console.WriteLine(item.Text);
}
}
public bool CanDrop()
{
return SelectedTab != null;
}
public void RemoveTab()
{
DropCourseConfirmation dcc = new DropCourseConfirmation();
dcc.Owner = mainWindow;
dcc.ShowDialog();
if (dcc.drop == true)
{
int index = AllTabs.IndexOf(SelectedTab);
AllTabs.Remove(SelectedTab);
if (AllTabs.Count > 0)
{
if (index == 0)
{
SelectedTab = AllTabs[0];
}
else
{
SelectedTab = AllTabs[--index];
}
}
else
{
SelectedTab = null;
}
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Please let me know if you need more codes to help me.
Change your
<Setter Property="Content">
<Setter.Value>
<Grid>
<TextBox Text="{Binding Text}" FontSize="16" AcceptsReturn="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</TextBox>
</Grid>
</Setter.Value>
</Setter>
for this:
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBox Text="{Binding Text}" FontSize="16" AcceptsReturn="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</TextBox>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
Your DataContext isn't what you think it is. Read the error there. It states that "Text" is not a valid property on MyHomeworkViewModel which is true (As opposed to your MyHomeworkModel).
What you need to be modifying instead of the ItemContainerStyle is instead the ItemTemplate and the ContentTemplate which uses the appropriate object within your ItemsSource as its DataContext.
Additionally, the binding in your TextBox needs to be Text="{Binding Text, Mode=TwoWay}" or it won't modify the property in your model.

Categories

Resources