WPF Binding a Main Window Control from a UserControl View Model - wpf

So as mentioned in an earlier question I asked WPF Data Binding From UserControl I have succesfulyl binded the TabHeader of my Control based off a value inside my UserControls code behind file using DependencyProperty, and acheived a a similar implementation with INotifyPropertyChanged.
However I now need it to work off the value from the UserControls ViewModel. I can succesfully update the UserControl UI using INotifyPropertyChanged but I am unable to bind this value to the TabItem control in the Main Window as it seems to regonise it.
Is this even possibly or am I barking up the wrong tree?
Main Window (TabControl) <---> UserControl <---> ViewModel
MainWindow.xaml
<Grid>
<TabControl Height="250" HorizontalAlignment="Left" Margin="12,26,0,0" Name="tabControl1" VerticalAlignment="Top" Width="479">
<TabControl.Resources>
<Style TargetType="TabItem" x:Key="tab1ItemHeaderStyle" >
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate DataType="{x:Type TabItem}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=Header, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabItem}}"/>
<Label Content="{Binding Path=SomeFigureVM, ElementName=uc1}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Style="{StaticResource tab1ItemHeaderStyle}" Header="[Tab 1]" Name="tabItem1">
<vw:UserControl1 x:Name="uc1"></vw:UserControl1>
</TabItem>
</TabControl>
</Grid>
UserControl1.xaml
<Grid>
<Label Height="43" HorizontalAlignment="Left" Margin="69,128,0,0" Name="textBlock" Content="{Binding SomeFigureVM}" VerticalAlignment="Top" Width="100" />
<Button Name="updateSomeFigure" Content="Update.." Click="updateSomeFigure_Click" Width="100" Height="100" Margin="69,12,66,71" />
</Grid>
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
private void updateSomeFigure_Click(object sender, RoutedEventArgs e)
{
MyViewModel viewmodel = this.DataContext as MyViewModel;
viewmodel.UpdateFigure();
}
}
MyViewModel.cs
public class MyViewModel: INotifyPropertyChanged
{
public MyViewModel()
{
this.SomeFigureVM = 23;
}
private int _someFigure;
public int SomeFigureVM
{
get
{
return _someFigure ;
}
set
{
_someFigure = value;
NotifyPropertyChanged("SomeFigureVM");
}
}
public void UpdateFigure()
{
SomeFigureVM = SomeFigureVM + 1;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
As always, any help is greatly appreciated, I feel like I've been smashing my head against a brick wall on this one!

SomeFigureVM is a property on your MyViewModel, which is the DataContext for UserControl1. You are trying to access SomeFigureVM prperty on UserControl, which doesn't exist.
Change this line:
<Label Content="{Binding Path=SomeFigureVM, ElementName=uc1}"/>
to
<Label Content="{Binding Path=DataContext.SomeFigureVM, ElementName=uc1}"/>
To catch data binding errors like this, run the application in debug mode and watch the output window for any data binding issues. Your original code generates a data binding error like:
System.Windows.Data Error: 40 : BindingExpression path error:
'SomeFigureVM' property not found on 'object' ''UserControl1'
(Name='uc1')'. BindingExpression:Path=SomeFigureVM;
DataItem='UserControl1' (Name='uc1'); target element is 'Label'
(Name=''); target property is 'Content' (type 'Object')

Related

wpf Databinding using XAML not working

I am developing a small application for learning purpose. I find that when I bind ItemControl's ItemSource to a ViewModel property in XAML, it doesn't work in an expected way. i.e. It loads the underlying collection with values at the loading time, but any changes to it are not reflected.
However, if I set Itemsource in Codebehind, it works.
When the form is loaded, it shows 2 note objects. Clicking on button should show the 3rd one. I don't understand why setting DataContext using XAML doesn't update to changes in collection. I am sharing snippet of the code here. Any help greatly appreciated.
Cut-down version of XAML -
<Window x:Class="NotesApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:NotesApp"
xmlns:vm="clr-namespace:NotesApp.ViewModel"
Title="MainWindow" Height="480" Width="640">
<Window.DataContext >
<vm:MainViewModel/>
</Window.DataContext>
<DockPanel >
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl Name="NoteItemControl" ItemsSource="{Binding notes}" Background="Beige" >
<ItemsControl.LayoutTransform>
<ScaleTransform ScaleX="{Binding Value, ElementName=zoomSlider}" ScaleY="{Binding Value, ElementName=zoomSlider}" />
</ItemsControl.LayoutTransform>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Name="NoteBorder" Background="Green" CornerRadius="3" Margin="5,3,5,3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding noteText}" Margin="5,3,5,3"/>
<StackPanel Grid.Row="1" Orientation="Vertical" >
<Line X1="0" Y1="0" X2="{Binding ActualWidth,ElementName=NoteBorder}" Y2="0" Stroke="Black" StrokeThickness="1"/>
<TextBlock Text="{Binding Category}" Margin="5,3,5,3"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Window>
View Code behind-
namespace NotesApp
{
public partial class MainWindow : Window
{
MainViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new MainViewModel();
// IT WORKS IF I BRING IN THIS STATEMENT
//NoteItemControl.ItemsSource = ViewModel.notes;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
ViewModel.AddNote(new Note("note3", "Category 3"));
}
}
}
ViewModel -
namespace NotesApp.ViewModel
{
public class MainViewModel: INotifyPropertyChanged
{
ObservableCollection<Note> _notes;
public ObservableCollection<Note> notes
{
get
{ return _notes; }
set
{
_notes = value;
OnPropertyChanged("notes");
}
}
public void AddNote(Note note)
{
_notes.Add(note);
OnPropertyChanged("notes");
}
public MainViewModel ()
{
notes = new ObservableCollection<Note>();
notes.Add(new Note("note1", "Category 1"));
notes.Add(new Note("note2", "Category 2"));
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs( propertyName));
}
}
}
You create a MainViewModel instance and assign it to the MainWindow's DataContext in XAML
<Window.DataContext >
<vm:MainViewModel/>
</Window.DataContext>
The bindings in your XAML use this instance as their source object, as long as you do not explicitly specify some other source. So there is no need (and it's an error) to create another instance in code behind.
Change the MainWindow's constructor like this:
public MainWindow()
{
InitializeComponent();
ViewModel = (MainViewModel)DataContext;
}
Try this :
<Window.Resources>
<vm:MainViewModel x:Key="mainVM"/>
</Window.Resources>
Now use this key as a static resource wherever you bind something like :
<ItemsControl Name="NoteItemControl" ItemsSource="{Binding notes,Source={StaticResource mainVM},Mode=TwoWay}" Background="Beige" >
If you do this, you dont need any datacontext

Using MVVM Light EventToCommand In DataTemplate

So I have a WPF UserControl:
<UserControl x:Class="BI_Builder.Views.ObjectTreeView"
x:Name="UC1"
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:BI_Builder"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:viewModels="clr-namespace:BI_Builder.ViewModels"
xmlns:command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" DataContext="{Binding}">
<UserControl.Resources>
<ContentControl x:Key="Context" Content="{Binding}" />
<DataTemplate x:Key="DataSourceTemplate">
<TextBlock Text="{Binding Path=Name}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<command:EventToCommand Command="{Binding Path=DataContext.OpenCommand, Mode=OneWay,ElementName=UC1}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
<HierarchicalDataTemplate x:Key="ItemTemplate"
ItemsSource="{Binding Children}"
ItemTemplate="{StaticResource DataSourceTemplate}">
<StackPanel>
<TextBlock Text="{Binding Header}">
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<Grid>
<TreeView Name="TreeView" ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ItemTemplate}" >
</TreeView>
</Grid>
</UserControl>
And here's the main view model for the user control:
public class ObjectTreeViewModel : ObservableObject {
public ObservableCollection<ItemViewModel> Items {
get {
if (_items != null) return _items;
_items = new ObservableCollection<ItemViewModel>();
_items.Add(DataSources);
return _items;
}
set { _items = value;
}
}
public ItemViewModel DataSources {
get { return _dataSources ?? (_dataSources = new ItemViewModel() { Header = "Data Sources", Children = new ObservableCollection<object>(DataSourceList) }); }
set { _dataSources = value; }
}
public List<DataSource> DataSourceList;
public ICommand OpenCommand {
get { if (_openCommand == null) { return _openCommand = new RelayCommand(OpenDataSource); } return _openCommand; }
}
private void OpenDataSource() {
MessageBox.Show("Test");
}
public ObjectTreeViewModel() {
DataSourceList = new List<DataSource>();
DataSourceList.Add(new DataSource() { Name = "Test" });
}
private ItemViewModel _dataSources;
private ObservableCollection<ItemViewModel> _items;
private RelayCommand _openCommand;
}
}
I've tried every method I've come across on the web to get the EventToCommand in the DataSourceTemplate DataTemplate to fire. In fact, I'm pretty sure it knows where the OpenCommand is, because if I change the Path to gobbledygook, the Output window throws me an error saying that "ObjectTreeView" (which is the instance of the ObjectTreeViewModel view model being bound to the UserControl) doesn't have the gobbledygook property. So I think I've set the DataContext correctly ...
But whenever I click on the text blocks ... nothing.
Really trying to avoid code-behind (it just feels wrong), and full disclosure, I'm using MVVM Light's EventToCommand but not the full toolkit, although I'm tempted to rewrite what I have so far in it to see if using the Service Locator will solve this problem.
The TextBlock control does not have a Click event. See MSDN.
You should use the MouseLeftButtonDown event instead:
<i:EventTrigger EventName="MouseLeftButtonDown">
<!-- ... -->
</i:EventTrigger>
Can you put a hyperlink inside your textblock instead and bind the command to the hyperlink?
Note you can style the hyperlink to look like a plain textblock if needed.
<TextBlock>
<Hyperlink Command="{Binding Path=DataContext.OpenCommand" Text="{Binding Path=Name}" />
</TextBlock>
Also make sure that the ObjectTreeView class is instantiated and loaded into DataContext of the usercontrol.

Two views of the same ViewModel within a user control

I have a re-usable usercontrol with a viewmodel behind it. I'm trying to switch between different views of the same data. Currently trying to use a Mode property on the VM to accomplish this.
I've created a DataTemplateSelector like so:
<UserControl x:Class="MyUserControl">
<UserControl.Resources>
<DataTemplate x:Key="ColumnTemplate">
<StackPanel>
<Label Text="{Binding Name}"></Label>
<Label Text="{Binding Address}"></Label>
<Label Text="{Binding Occupation}"></Label>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="AvatarTemplate">
<StackPanel>
<Image Source="{Binding ProfilePicture}"></Image>
<Label Text="{Binding Name}"></Label>
</StackPanel>
</DataTemplate>
<local:DisplayTemplateSelector ColumnTemplate="{StaticResource ColumnTemplate}" AvatarTemplate="{StaticResource AvatarTemplate}" x:Key="displayTemplateSelector" />
</UserControl.Resources>
<Grid>
<ContentControl Name="cpDisplay" Content="{Binding}" ContentTemplateSelector="{StaticResource displayTemplateSelector}" />
</Grid>
</UserControl>
With the class:
class DisplayTemplateSelector : DataTemplateSelector
{
public DataTemplate ColumnTemplate {get;set;}
public DataTemplate AvatarTemplate {get;set;}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
MainViewModel vm = (MainViewModel)item;
switch (vm.Mode)
{
case MainViewModel.DisplayMode.Column:
return ColumnTemplate;
case MainViewModel.DisplayMode.Avatar:
return AvatarTemplate;
default:
return AvatarTemplate;
}
}
}
This usercontrol sits in MyWindow:
<Grid>
<controls:MyUserControl x:Name="MyUserControl" DataContext="{Binding}" Margin="0"/>
</Grid>
Which is instantiated with my viewmodel:
MyWindow w = new MyWindow(_vm);
w.Show();
The problem I have is that item is null during MainViewModel vm = (MainViewModel)item. It's like I'm trying to set the datatemplate based on data, before the data is bound?
Is there anyway to choose the desired datatemplate not based on the dataobject - but as a property or similar on the usercontrol?
There are many ways, but here are a couple:
<!-- assumes you have a data template selector implementation available as resource MyContentSelector -->
<ContentControl Content="{StaticResource MainViewModel}" ContentTemplateSelector="{StaticResource MyContentSelector}"/>
or:
<!-- assumes you have appropriate boolean properties on your VM -->
<Grid>
<ContentControl Content="{StaticResource MainViewModel}" ContentTemplate="{StaticResource column}" Visibility="{Binding IsColumnVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<ContentControl Content="{StaticResource MainViewModel}" ContentTemplate="{StaticResource avatar}" Visibility="{Binding IsAvatarVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
See DataTemplateSelector Class
DataTemplateSelector
Ok, finally got this to work how I needed by using a property on the usercontrol and some code behind:
public enum DisplayMode
{
Column,
Avatar
}
public DisplayMode Mode { get; set; }
public MyUserControl()
{
InitializeComponent();
Mode = DisplayMode.Avatar;
}
private void MyUserControl_Loaded(object sender, RoutedEventArgs e)
{
switch (Mode)
{
case DisplayMode.Column:
cpDisplay.ContentTemplate = (DataTemplate)this.Resources["ColumnTemplate"];
cpDisplay.ApplyTemplate();
break;
case DisplayMode.Avatar:
cpDisplay.ContentTemplate = (DataTemplate)this.Resources["AvatarTemplate"];
cpDisplay.ApplyTemplate();
break;
}
}
I removed the DataTemplateSelector code and simply defined the datatemplates and used:
<ContentPresenter Name="cpDisplay" Content="{Binding}" />

How to get WPF DataBinding-to-an-object to work

In the following example I bind the XAML to a static object via ObjectDataProvider.
When the user changes information, I want it to automatically reflect in the XAML.
What I don't understand is:
how do I perpetuate the object? do I have to create a singleton? in the click event, how do I access the "object that is being edited"
eventually of course I want the data to be retrieved from a model which reads an XML file or web service, and I want of course my ViewModel to check my model every second or so to see if the data has changed and reflect this on the XAML.
How to I get THERE from HERE:
XAML:
<Window x:Class="TestBinding99382.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestBinding99382"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider
x:Key="DataSourceCustomer"
ObjectType="{x:Type local:Customer}" MethodName="GetCustomer"/>
<Style x:Key="DataRowStyle" TargetType="StackPanel">
<Setter Property="Orientation" Value="Horizontal"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Margin" Value="0 10 0 0"/>
<Setter Property="DataContext"
Value="{StaticResource DataSourceCustomer}"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</Window.Resources>
<DockPanel>
<StackPanel DockPanel.Dock="Top"
DataContext="{StaticResource DataSourceCustomer}"
Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding Path=LastName}"/>
<TextBlock Text=" ("/>
<TextBlock Text="{Binding Path=FullName}" FontWeight="Bold"/>
<TextBlock Text=")"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<TextBlock Text="First Name:"/>
<TextBox Text="{Binding Path=FirstName}"
Width="200" Margin="3 0 0 0"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<TextBlock Text="Last Name:"/>
<TextBox Text="{Binding Path=LastName}"
Width="200" Margin="3 0 0 0"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<Button Content="Save Changes" Click="Button_Click"/>
</StackPanel>
</DockPanel>
</Window>
Code Behind:
using System.Windows;
using System.ComponentModel;
using System;
namespace TestBinding99382
{
public partial class Window1 : Window
{
private Customer _customer;
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//I want to edit the _customer object here
//and have it my changes automatically reflect in my XAML
//via the INotifyPropertyChanged inheritance.
}
}
public class Customer : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
this.RaisePropertyChanged("FirstName");
this.RaisePropertyChanged("FullName");
}
}
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
this.RaisePropertyChanged("LastName");
this.RaisePropertyChanged("FullName");
}
}
public string FullName
{
get
{
return String.Format("{0} {1}", _firstName, _lastName);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public static Customer GetCustomer()
{
return new Customer { FirstName = "Jim", LastName = "Smith" };
}
}
}
in the click event, how do I access
the "object that is being edited"
You can access a resource in the behind code using FindResource method, see below.
private void Button_Click(object sender, RoutedEventArgs e)
{
ObjectDataProvider objectDataProvider
= FindResource("DataSourceCustomer") as ObjectDataProvider;
_customer = objectDataProvider.Data as Customer;
}
For your other questions:
What is perpetuate? You do not have to create a singleton to databind in WPF if that is your question.
eventually of course I want the data to be retrieved from a model which reads an XML file or web service, and I want of course my ViewModel to check my model every second or so to see if the data has changed and reflect this on the XAML.
WPF databinding will automatically update your view of you use an INotifyPropertyChanged object. Unless for performance reasons you only want to update your view every second or so just stick to normal databinding.

WPF User control binding issue

This should be a very simple case, but I am pulling hair trying to get it to work. Here is the setup:
I am designing an app that will have an read-only mode and edit mode for some data. So I created a User Control which is a textbox and textblock bound to the same text data and are conditionally visible based on EditableMode property (so when it's editable the textbox is shown and when it's not the textblock is shown)
Now, I want to have many of these controls in my main window and have them all bound too a single bool property. When that property is changed via a button, I want all TextBlocks to turn into TextBoxes or back.
My problem is that the control is set correctly on binding, and if I do myUserControl.Editable = true. But it doesn't change if bind it to a bool property.
Here is the code for my user control:
<UserControl x:Class="CustomerCareTool.Controls.EditableLabelControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:CustomerCareTool.Converters"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<UserControl.Resources>
<src:BoolToVisibility x:Key="boolToVisibility" Inverted="False" />
<src:BoolToVisibility x:Key="invertedBoolToVisibility" Inverted="True" />
</UserControl.Resources>
<Grid>
<TextBlock Name="textBlock" Text="{Binding Path=TextBoxValue}" Visibility="{Binding Path=EditableMode, Converter={StaticResource invertedBoolToVisibility}}"/>
<TextBox Name="textBox" Visibility="{Binding Path=EditableMode, Converter={StaticResource boolToVisibility}}">
<TextBox.Text>
<Binding Path="TextBoxValue" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</Grid>
I used a converter to convert bool to visibility and inverse bool to visibility. Not sure if that's at all needed here.
And this is the code behind:
public partial class EditableLabelControl : UserControl
{
public EditableLabelControl()
{
InitializeComponent();
}
public string TextBoxValue
{
get { return (string)GetValue(TextBoxValueProperty); }
set { SetValue(TextBoxValueProperty, value); }
}
public static readonly DependencyProperty TextBoxValueProperty =
DependencyProperty.Register("TextBoxValue", typeof(string), typeof(EditableLabelControl), new UIPropertyMetadata());
public bool EditableMode
{
get { return (bool)GetValue(EditableModeProperty); }
set { SetValue(EditableModeProperty, value); }
}
public static readonly DependencyProperty EditableModeProperty =
DependencyProperty.Register("EditableMode", typeof(bool),typeof(EditableLabelControl), new UIPropertyMetadata(false, EditableModePropertyCallBack));
static void EditableModePropertyCallBack(DependencyObject property,
DependencyPropertyChangedEventArgs args)
{
var editableLabelControl = (EditableLabelControl)property;
var editMode = (bool)args.NewValue;
if (editMode)
{
editableLabelControl.textBox.Visibility = Visibility.Visible;
editableLabelControl.textBlock.Visibility = Visibility.Collapsed;
}
else
{
editableLabelControl.textBox.Visibility = Visibility.Collapsed;
editableLabelControl.textBlock.Visibility = Visibility.Visible;
}
}
}
Now in my main application I have the control added like this:
<Controls:EditableLabelControl x:Name="testCtrl" EditableMode="{Binding Path=Editable}" TextBoxValue="John Smith" Grid.Row="0"/>
For that same application the DataContext is set to self
DataContext="{Binding RelativeSource={RelativeSource Self}}"
And the code behind looks like this:
public partial class OrderInfoView : Window, INotifyPropertyChanged
{
public OrderInfoView()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Editable = !Editable;
}
private bool _editable = false;
public bool Editable
{
get
{
return _editable;
}
set
{
_editable = value;
OnPropertyChanged("Editable");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Clicking the button doesn't do anything :( I tried everything to get this to work, and no dice. Would really appreciate some help!
I tried the following, and still does not work:
public bool Editable
{
get { return (bool)GetValue(EditableProperty); }
set { SetValue(EditableProperty, value); }
}
public static readonly DependencyProperty EditableProperty =
DependencyProperty.Register("Editable", typeof(bool), typeof(OrderInfoView), new UIPropertyMetadata(false));
It looks like your solution may be more complex than necessary. If all you want to do is have a disabled TextBox look like a TextBlock then you can do this using a trigger and a template. Then you can apply that style to all text boxes.
Here's an example of that approach:
<Window x:Class="WpfApplication25.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Height="300"
Width="300"
>
<Window.Resources>
<!-- Disable TextBox Style -->
<Style x:Key="_DisableTextBoxStyle" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<!--
Be sure to apply all necessary TemplateBindings between
the TextBox and TextBlock template.
-->
<TextBlock Text="{TemplateBinding Text}"
FontFamily="{TemplateBinding FontFamily}"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBox IsEnabled="{Binding IsChecked, ElementName=uiIsEnabled}"
Style="{StaticResource _DisableTextBoxStyle}"
/>
<ToggleButton x:Name="uiIsEnabled" Content="Enable" IsChecked="True" />
</StackPanel>
</Window>
INotifyPropertyChanged does not work for classes that derive from DependencyObject.
Editable property in OrderInfoView must be dependency property in order for binding to work correctly, although technically your code is correct but I feel its bug in WPF that when object is dependency object it ignores INotifyPropertyChanged event because it is searching for notification in property system.
<Controls:EditableLabelControl x:Name="testCtrl"
EditableMode="{Binding Path=Editable,ElementName=userControl}" TextBoxValue="John Smith" Grid.Row="0"/>
Specify ElementName in binding tag and also name your usercontrol with x:FieldName or x:Name
I just came across this searching for something else.
Without reading your post in detail (no time atm sorry) it seems to me you're having a similar issue to the one I posted about here:
http://jonsblogat.blogspot.com/2009/11/wpf-windowdatacontext-and.html
In short, move your binding for your main window to the Grid and use a relative binding to see if that fixes your problem.

Resources