I would like to bind the IsEnabled property of TabItem to data within my code.
e.g. I have a TabItem defined as follows
<TabItem Name="Tab1" Header="Tab1" IsEnabled="{Binding Path=Tab1Enabled, Mode=TwoWay}">
</TabItem>
And I have defined a data class which inherits from INotifyPropertyChanged as follows
class MyData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public bool Tab1Enabled
{
get{ return m_tab1Enabled; }
set
{
m_tab1Enabled = value;
OnPropertyChanged("Tab1Enabled");
}
}
}
Then I set the DataContext to my data member.
public partial class MyApp : Window
{
MyData m_myData = new MyData();
MyApp()
{
InitializeComponent();
this.DataContext = m_myData;
}
}
However, when the Tab1Enabled property gets set programatically, the PropertyChanged event is null and so the notification event is not sent.
Thanks in advance.
Im guessing you need to change m_bindinData to the variable you want to bind to (m_myData). I fired up VS2012 and tested your code. Setting m_myData.Tab1Enabled = true; set the tab to enabled and setting m_myData.Tab1Enabled = false; disabled it correctly. Heres what I had.
public partial class MyApp : Window
{
MyData m_myData = new MyData();
MyApp()
{
this.DataContext = m_myData;
InitializeComponent();
m_myData.Tab1Enabled = true;
}
}
class MyData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public bool Tab1Enabled
{
get { return m_tab1Enabled; }
set
{
m_tab1Enabled = value;
OnPropertyChanged("Tab1Enabled");
}
}
private bool m_tab1Enabled;
}
<TabControl>
<TabItem Name="Tab1" Header="Tab1" IsEnabled="{Binding Path=Tab1Enabled}">
</TabItem>
<TabItem Name="Tab2" Header="Tab2">
</TabItem>
</TabControl>
What about this? Notice the PropertyChangedEventHandler PropertyChanged = PropertyChanged; line in the OnPropertyChanged method.
class MyData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler PropertyChanged = PropertyChanged;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public bool Tab1Enabled
{
get{ return m_tab1Enabled; }
set
{
m_tab1Enabled = value;
OnPropertyChanged("Tab1Enabled");
}
}
}
Your TabItem might not inheriting DataContext of your Window. Try looking into the output window, binding failure error will be there.
As a workaround you can use RelativeSource to travel upto the DataContext of your window and bind with its related property like this -
<TabItem Name="Tab1" Header="Tab1"
IsEnabled="{Binding Path=DataContext.Tab1Enabled,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window}, Mode=TwoWay}"/>
Related
I am struggling with this for a while and I cannot figure it out. I have a button and a textBox. The textBox is linked to a property named: MessageDisplay. I want to be able to access this property and update the textBox in several places. Sadly, the PropertyChanged is null. The weird thing is that if I copy/paste the MessageDisplayModel class into the *MessageViewModel * class, it works ...
here is my code :
XAMLfile :
<Grid>
<Button Command="{Binding DisplayTextCommand}" Name="DisplayTextCommand" Margin="53,72,544.6,286" Width="Auto">Push</Button>
<TextBox Name="MessageDisplay" Text="{Binding MessageDisplay, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</Grid>
MessageDisplayModel file
public class MessageDisplayModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _message;
public string MessageDisplay
{
get { return _message; }
set
{
this._message = value;
this.OnPropertyChanged("MessageDisplay");
}
}
public void UpdateTextBox(string output)
{
MessageDisplay = output;
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}//class
MessageViewModel file:
public class MessageViewModel
{
private ICommand _testCommand;
public MessageDisplayModel MessageDisplaySmt = new MessageDisplayModel();
public ICommand DisplayTextCommand
{
get
{
return new DelegateCommand(DisplayMessage);
}
set
{
if (_testCommand == value) return;
_testCommand = value;
}
}
public void DisplayMessage()
{
MessageDisplaySmt.UpdateTextBox("Successfuly downloaded");
}
}//class
MainWindow file
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MessageDisplay.DataContext = new MessageDisplayModel();
DisplayTextCommand.DataContext = new MessageViewModel();
}
}//class
I update the MessageDisplay property by using the method UpdateTextBox(string). I call this method on the click of the button. When debugging the property gets updated but when time comes to notify the UI that the property has changed, PropertyChangedEventHandler PropertyChanged has its value null ... But if I write something in the textBox, the PropertyChangedEventHandler PropertyChanged gets changed and isn't null anymore. All I want is to be able to change the textBox's property whenever I want and from anywhere I want to.
Thank you
You are using two different instances of MessageDisplayModel. You must use a shared instance.
Also the DisplayTextCommand is implemented "wrong". The set method is redundant as the property's get always returns a new instance of the ICommand.
MessageViewModel.cs
public class MessageViewModel
{
pulic MessageViewModel()
{
}
pulic MessageViewModel(MessageDisplayViewModel messageDisplayViewModel)
{
this.MessageDisplaySmt = messageDisplayViewModel;
}
public void DisplayMessage()
{
this.MessageDisplaySmt.UpdateTextBox("Successfuly downloaded");
}
public MessageDisplayViewModel MessageDisplaySmt { get; set; }
public ICommand DisplayTextCommand { get => new DelegateCommand(DisplayMessage); }
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Alternatively use XAML to set the DataContext (see MainWindow.xaml). Would require a parameterless constructor.
this.DataContext = new MessageViewModel(new MessageDisplayViewModel());
}
}
MainWindow.xaml
<Window>
<!--
Alternative DataContext declaration using XAML instead of C#.
Requires a parameterless constructor for both view model objects.
-->
<Window.DataContext>
<MessageViewModel>
<MessageViewModel.MessageDisplaySmt>
<MessageDisplayViewModel />
</MessageViewModel.MessageDisplaySmt>
</MessageViewModel>
</Window.DataContext>
<StackPanel>
<Button Command="{Binding DisplayTextCommand}"
Content="Push" />
<TextBox Text="{Binding MessageDisplaySmt.MessageDisplay}" />
</StackPanel>
</Window>
I'm working with WPF using Prism ( MVVM). I wanted to set visibililty
of StackPanel from ViewModel calss. The StackPanel's visibility is
binded like :
<StackPanel x:Name="spVisibility" Orientation="Horizontal"
Visibility="{Binding spVisibility, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
I've view model class like :
public class SearchId : BindableBase, INotifyPropertyChanged
{
private Visibility _visibility = Visibility.Collapsed;
private DelegateCommand<object> searchCommand;
public event PropertyChangedEventHandler PropertyChanged;
public SearchId()
{
searchCommand = new DelegateCommand<object>(this.SearchData);
}///
public Visibility spVisibility
{
get { return _visibility; }
set
{
if (!string.Equals(_visibility, value))
{
_visibility = value;
RaisePropertyChanged("spVisibility");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs (propertyName));
}
}
private async void SearchData(object parameter)
{
_visibility = Visibility.Visible;
}
}
But this not working. Please help me.
_visibility = Visibility.Visible is setting the private property instead of using the public one so RaisePropertyChanged("spVisibility") is being bypassed. You need to use spVisibility = Visibility.Visible.
If you are using MVVM i would recommend using a Boolean value instead of Visibility. The whole purpose of MVVM is seperation of View Logic from DataLogic.
View logic:
<StackPanel Orientation="Horizontal"
Visibility="{Binding ShowStackPanel, Converter={StaticResource BooleanToVisibilityConverter}}">
Use a Converter to convert the boolan to a Visibility Property.. BooleanToVisibilityConverter is part of .NET and can be referenced without defining it manually in the xaml.
public class SearchId : BindableBase, INotifyPropertyChanged
{
private bool _showStackPanel;
private DelegateCommand<object> searchCommand;
public event PropertyChangedEventHandler PropertyChanged;
public SearchByIDVM()
{
searchCommand = new DelegateCommand<object>(this.SearchData);
}///
public bool ShowStackPanel
{
get { return _showStackPanel; }
set
{
if (!Equals(_showStackPanel, value))
{
_showStackPanel= value;
RaisePropertyChanged("ShowStackPanel");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs (propertyName));
}
}
private async void SearchData(object parameter)
{
ShowStackPanel= true;
}
}
I am using WPF and MVVM light framework (and I am new in using them)
Here is the situation:
I have a combobox displaying a list of items (loaded from a database) and I am using the DisplayMemberPath to display the title of the items in the combobox.
In the same GUI, the user can modify the item title in a text box. A button 'Save' allows the user to save the data into the database.
What I want to do is when the user clicks 'Save', the item title in the combobox gets updated too and the new value is displayed at that time. However, I do not know how to do that...
Some details on my implementation:
MainWindow.xaml
<ComboBox ItemsSource="{Binding SourceData}" SelectedItem="{Binding SelectedSourceData,Mode=TwoWay}" DisplayMemberPath="Title" />
<TextBlock Text="{Binding SelectedDataInTextFormat}"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Closing += (s, e) => ViewModelLocator.Cleanup();
}
}
MainViewModel.xaml
public class MainViewModel:ViewModelBase
{
public ObservableCollection<Foo> SourceData{get;set;}
public Foo SelectedSourceData
{
get{return _selectedFoo;}
set{_selectedFoo=value; RaisePropertyChanged("SelectedSourceData"); }
}
public string SelectedDataInTextFormat
{
get{return _selectedDataInTextFormat;}
set{_selectedDataInTextFormat=value; RaisePropertyChanged("SelectedDataInTextFormat");
}
}
I would appreciate if anyone could help me on this one.
Thanks for your help.
Romain
You might simply update the SelectedSourceData property when SelectedDataInTextFormat changes:
public string SelectedDataInTextFormat
{
get { return _selectedDataInTextFormat; }
set
{
_selectedDataInTextFormat = value;
RaisePropertyChanged("SelectedDataInTextFormat");
SelectedSourceData = SourceData.FirstOrDefault(f => f.Title == _selectedDataInTextFormat)
}
}
EDIT: In order to change the Title property of the currently selected Foo item in the ComboBox, you could implement INotifyPropertyChanged in your Foo class:
public class Foo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string title = string.Empty;
public string Title
{
get { return title; }
set
{
title = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Title"));
}
}
}
}
Then simply set the Title property of the selected item:
SelectedSourceData.Title = SelectedDataInTextFormat;
There is many ways to do this, This example takes advantage of the Button Tag property to send some data to the save button handler(or ICommand), Then we can set the TextBox UpdateSourceTrigger to Explicit and call the update when the Button is clicked.
Example:
Xaml:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="105" Width="156" Name="UI">
<Grid DataContext="{Binding ElementName=UI}">
<StackPanel Name="stackPanel1">
<ComboBox x:Name="combo" ItemsSource="{Binding SourceData}" DisplayMemberPath="Title" SelectedIndex="0"/>
<TextBox x:Name="txtbox" Text="{Binding ElementName=combo, Path=SelectedItem.Title, UpdateSourceTrigger=Explicit}"/>
<Button Content="Save" Tag="{Binding ElementName=txtbox}" Click="Button_Click"/>
</StackPanel>
</Grid>
</Window>
Code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<Foo> _sourceData = new ObservableCollection<Foo>();
public MainWindow()
{
InitializeComponent();
SourceData.Add(new Foo { Title = "Stack" });
SourceData.Add(new Foo { Title = "Overflow" });
}
public ObservableCollection<Foo> SourceData
{
get { return _sourceData; }
set { _sourceData = value; }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var txtbx = (sender as Button).Tag as TextBox;
txtbx.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class Foo : INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set { _title = value; RaisePropertyChanged("Title"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Code:
public ObservableCollection<Foo> SourceData{get;set;}
public Foo SelectedSourceData
{
get{
return _selectedFoo;
}
set{
_selectedFoo=value;
RaisePropertyChanged("SelectedSourceData");
}
}
public string SelectedDataInTextFormat //Bind the text to the SelectedItem title
{
get{
return SelectedSourceData.Title
}
set{
SelectedSourceData.Title=value;
RaisePropertyChanged("SelectedDataInTextFormat");
}
}
XAML:
<ComboBox ItemsSource="{Binding SourceData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedSourceData,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Title" />
<TextBlock Text="{Binding SelectedDataInTextFormat, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
I have question about DataContext changes and I build example for understand this aspect.
I have MainUserControl on MainWindow. MainUserControl consists of number of User Controls.
One of such User Controls is SubUserControl1.
<Window x:Class="WpfApplicationUcBindingQuestion.MainWindow">
<Grid>
.....
<uc:MainUserControl />
</Grid>
</Window>
<UserControl x:Class="WpfApplicationUcBindingQuestion.MainUserControl">
<Grid>
.....
<uc:SubUserControl1 x:Name="subUserControl1" />
</Grid>
</UserControl>
And in MainWindow I have object of class Info. Class Info consists of a few inner classes.
One of them is, lets say, SubInfo. Both Info and SubInfo classes inherits from INotifyPropertyChanged.
And this is the code of them:
public class Info : INotifyPropertyChanged
{
private SubInfo m_subInfo = new SubInfo();
public Info()
{
}
public SubInfo SubInfo
{
get
{
return m_subInfo;
}
set
{
m_subInfo = value;
OnPropertyChanged("SubInfo");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SubInfo: INotifyPropertyChanged
{
private string m_subString = "subStr";
public SubInfo()
{
}
public string SubString
{
get
{
return m_subString;
}
set
{
m_subString = value;
OnPropertyChanged("SubString");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I want to set DataContext for MainUserControl to be object of class Info
and for SubUserControl1 DataContext will be Info.SubInfo.
The following code describes this:
<UserControl x:Class="WpfApplicationUcBindingQuestion.SubUserControl1">
<Grid>
.....
<TextBox Text="{Binding Path=SubString}"/>
</Grid>
</UserControl>
public MainUserControl()
{
InitializeComponent();
MainWindow mainWnd = (MainWindow)Application.Current.MainWindow;
Info info = mainWnd.Info;
this.DataContext = info;
this.subUserControl1.DataContext = info.SubInfo;
}
When new subInfo arrived I update inner object subInfo inside info object:
(This is function of MainWindow)
private void OnUpdateData()
{
SubInfo arrivedSubInfo = new SubInfo();
arrivedSubInfo.SubString = "newString";
m_info.SubInfo = arrivedSubInfo;
}
I want to see that DataContext for subUserControl1 is also changed.
But it doesn't happened and TextBox inside SubUserControl1 is not updated
and doesn't show "newString".
(Note: If I write inside OnUpdateData() function the following:
m_info.SubInfo.SubString = arrivedSubInfo.SubString;
(copy field-field and not whole object) it works,
but I dont 'want copy 50 field...)
Where I'm wrong?
Your help will be really appreciated.
Your problem is the following:
In your constructor, when you do that:
this.DataContext = info;
this.subUserControl1.DataContext = info.SubInfo;
You will set the DataContext ONLY once. Which means it will never change unless you write subUserControl1.DataContext = someNewDataContext somewhere.
What you can do to solve this :
The "proper solution":
Use a binding. In your XAML, just write:
<uc:SubUserControl1 x:Name="subUserControl1" DataContext="{Binding
SubInfo, UpdateSourceTrigger=PropertyChanged}" />
This will work, assuming that your SubInfo property fires the OnPropertyChanged event when it is set.
The "ugly solution":
Explicitly set your UserControl's DataContext in code-behind when you need it. Again, I wouldn't advise that and you are better off applying the first solution!
I have a textbox which I need to bind a string to.
<TextBox Name="txtDoc" Margin="5" Text ="{Binding Source={x:Static local:DocumentViewModel.FileText}, Path=FileText}">
The FileText property is changed on a different class:
DocumentViewModel.GetInstance().FileText = File.ReadAllText(document.Path);
The DocumentViewModel is a class with Singleton:
public class DocumentViewModel : INotifyPropertyChanged
{
private static string fileText;
public string FileText
{
get { return fileText; }
set
{
fileText = value; // Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("FileText");
}
}
private void OnPropertyChanged(string filetext)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(filetext));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private static DocumentViewModel instance = new DocumentViewModel();
private DocumentViewModel() { }
public static DocumentViewModel GetInstance()
{
return instance;
}
}
I need to be able to change the value of the FileText property and reflect this change in the textbox.
It's not working.
I tried using TextBox as a static property but then the Onp
Try to set the source to your viewmodel instead of the property itself, and set the instance property to public? {Binding Source={x:Static local:DocumentViewModel.instance}, Path=FileText}
Edit: Included a complete example, that working for me:
Xaml:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test"
Title="MainWindow" Height="350" Width="525"
Loaded="Window_Loaded">
<TextBox Name="txtDoc" Margin="5"
Text="{Binding Source={x:Static local:DocumentViewModel.Instance}, Path=FileText}" />
</Window>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DocumentViewModel.Instance.FileText = "Hello world!";
}
}
public class DocumentViewModel : INotifyPropertyChanged
{
#region Singleton implementation
// Static constructor to create the singleton instance.
static DocumentViewModel()
{
DocumentViewModel.Instance = new DocumentViewModel();
}
public static DocumentViewModel Instance { get; private set; }
#endregion
private static string fileText;
public string FileText
{
get { return fileText; }
set
{
if (fileText != value)
{
fileText = value;
OnPropertyChanged("FileText");
}
}
}
#region INotifyPropertyChanged
private void OnPropertyChanged(string filetext)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(filetext));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}