<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Tên Mặt Hàng" CellTemplate="{StaticResource Ten}">
<GridViewColumn.CellTemple>
<DataTemplate x:Key="Ten">
<TextBlock Text="{Binding Ten}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemple>
</GridViewColumn> </GridView>
every one, I have code, I want to binding Text of textBlock to use data again in other files, so what can I do?
You can create a ViewModel of this view. And bind the viewModel property to this view. For example:
public class NamViewModel: INotifyPropertyChanged {
private string _ten;
public string Ten {
get
{
return _ten;
}
set
{
_ten = value;
this.RaisePropertyChanged("Ten");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then you can bind this viewModel in the constructor of the view. For example:
public partial class NamView : Window {
public NamView() {
InitializeComponent();
this.DataContext = new NamViewModel();
}
}
Related
I have below class hierarchy.
public class RowViewModel : BaseViewModel
{
private CellViewModel cell;
public CellViewModel Cell
{
get { return cell; }
set
{
cell = value;
OnPropertyChanged("Cell");
}
}
}
public class CellViewModel : BaseViewModel
{
public string Text { get { return string.Join("\n", CellLinks.Select(c => c.Text)); } }
private ObservableCollection<CellLinkViewModel> cellLinks;
public ObservableCollection<CellLinkViewModel> CellLinks
{
get
{ return cellLinks; }
set
{
cellLinks = value;
OnPropertyChanged("CellLinks");
}
}
}
public class CellLinkViewModel : BaseViewModel
{
public string Text
{
get { return CellValue.Text; }
set
{
CellValue.Text = value;
OnPropertyChanged("Text");
}
}
public CellValueViewModel CellValue { get; set; }
}
public class CellValueViewModel : BaseViewModel
{
public string Text { get; set; }
}
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, e);
}
}
}
XAML code looks like,
<DataGrid ItemsSource="{Binding Rows}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="#" Width="200">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Cell.Text}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Cell.CellLinks}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Text, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
CellValueViewModel is shared across multiple CellLinkViewModel. When I change CellValueViewModel it should have propagated changes to all dependent parents and DataGrid should show all latest values.
This is result
But I think its not happening automatically, am I missing something here? How to notify all datagrid cells to update automatically when the nested objects gets updated.
You need to hook up event handlers to the PropertyChanged event for all CellLinkViewModel objects and raise the PropertyChanged event for the Text property of the CellViewModel whenever a link is modified, e.g.:
public class CellViewModel : BaseViewModel
{
public string Text { get { return string.Join("\n", CellLinks.Select(c => c.Text)); } }
private ObservableCollection<CellLinkViewModel> cellLinks;
public ObservableCollection<CellLinkViewModel> CellLinks
{
get
{
return cellLinks;
}
set
{
cellLinks = value;
if(cellLinks != null)
{
foreach(var link in cellLinks)
{
link.PropertyChanged += Link_PropertyChanged;
}
}
OnPropertyChanged("CellLinks");
}
}
private void Link_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("Text");
}
}
You probably also want to handle the CollectionChanged event for the ObservableCollection<CellLinkViewModel> and hook up event handlers to dynamically added CellLinkViewModel objects as well.
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}"/>
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 the following xaml;
<DataTemplate DataType="{x:Type NameSpace:Node}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Item.Value}"/>
<ContentControl Content="{Binding Item}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type NameSpace:Item}">
<TextBlock Text="{Binding Value}" />
</DataTemplate>
When I use the Node template to display a Node object, I get two TextBlocks which both display the same value. So far so good. The problem occurs when Item changes. When the Node class fires the INotifyPropertyChanged event, the TextBlock in the Node DataTemplate updates as expected but the TextBlock in the Item DataTemplate does not update.
How can I get the Item DataTemplate to update its bindings when the Node class fires the IPropertyChanged event?
Update
It turns out the above does work for the following simple scenario;
Xaml
<DataTemplate DataType="{x:Type DataTemplateExample:Node}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Item.Value}"/>
<ContentControl Content="{Binding Item}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type DataTemplateExample:Item}">
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<ContentControl Content="{Binding MyNode}"/>
<Button Command="{Binding ChangeMyNodeItem}">Change Item</Button>
</StackPanel>
</Grid>
</Window>
c#
public class MainViewModel
{
private readonly Node myNode;
private readonly DelegateCommand changeMyNodeItemCmd;
public MainViewModel()
{
myNode = new Node {Item = new Item("a")};
changeMyNodeItemCmd = new DelegateCommand(()=>
myNode.Item = new Item("b"));
}
public Node MyNode { get { return myNode; } }
public ICommand ChangeMyNodeItem
{
get { return changeMyNodeItemCmd; }
}
}
public class Node : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Item item;
public Item Item
{
set
{
item = value;
if (PropertyChanged != null)
PropertyChanged(this,new PropertyChangedEventArgs("Item"));
}
get { return item; }
}
}
public class Item
{
private readonly string value;
public Item(string value)
{
this.value = value;
}
public string Value
{
get { return value; }
}
}
In my real scenario I was using proxies, and I think this is what was getting WPF confused. Item was not actually changing - it was being remapped.
Ultimately I solved the problem using a solution similar to what ShadeOfGray proposed. But I should point out that this is not necessary unless you are using proxies.
From what you have posted I think you're firing the NotifyPropertyChanged in the wrong class. Something like that should work properly in your scenario.
Updated according to the comment:
public class Node : INotifyPropertyChanged
{
private Item item;
public event PropertyChangedEventHandler PropertyChanged;
public Item Item
{
get
{
return item;
}
set
{
item = value;
this.NotifyPropertyChanged("Item");
if (item != null)
{
item.ForcePropertyChanged("Value");
}
}
}
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Item : INotifyPropertyChanged
{
private string itemValue;
public Item()
{
this.Value = string.Empty;
}
public event PropertyChangedEventHandler PropertyChanged;
public string Value
{
get
{
return itemValue;
}
set
{
itemValue = value;
NotifyPropertyChanged("Value");
}
}
public void ForcePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Original question was here.
Binding To A ListView
I have fixed one issue and now see the column names. However, I can't figure out the binding.
The error from the output window:
System.Windows.Data Error: 4 : Cannot find source for binding with reference
'ElementName=This'. BindingExpression:Path=LogView.LogEntries; DataItem=null; target
element is 'ListView' (Name='LoggingListView'); target property is 'ItemsSource' (type 'IEnumerable')
Snippet of the XAML with my latest attempt from LogFileWindow.XAML. I can post more but trying to keep the clutter down:
<ListView Name="LoggingListView" ItemsSource="{Binding ElementName=This, Path=LogView.LogEntries} ">
<ListView.View>
<GridView>
<GridViewColumn Header="Date" DisplayMemberBinding="{Binding Path=Date}"></GridViewColumn>
<GridViewColumn Header="Time" DisplayMemberBinding="{Binding Path=Time}"></GridViewColumn>
<GridViewColumn Header="Event" DisplayMemberBinding="{Binding Path=Event}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The C# ViewModel:
public class LogEntryViewModel : INotifyPropertyChanged
{
public LogEntryViewModel(LogFileEntry le)
{
_date = le.Date;
_time = le.Time;
_event = le.Event;
}
#region Members
private string _date;
public string Date
{
get { return _date; }
set {_date = value;
RaisePropertyChanged("Date");
}
}
private string _time;
public string Time
{
get { return _time; }
set
{
_time = value;
RaisePropertyChanged("Time");
}
}
private string _event;
public string Event
{
get { return _event; }
set { _event = value;
RaisePropertyChanged("Event");
}
}
private LogFileEntry _le;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void RaisePropertyChanged(string propertyName)
{
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class LogViewModel :ObservableCollection<LogEntryViewModel>
{
private ObservableCollection<LogEntryViewModel> _LogEntries;
public ObservableCollection<LogEntryViewModel> LogEntries = new
ObservableCollection<LogEntryViewModel>();
}
Partial Class declaration and code-behind where I am trying to use it:
public partial class LogFileWindow : Window
{
public LogViewModel LogView = new LogViewModel();
}
The Visual Studio error is pretty self-evident and self explanatory: you're trying to do Binding with ElementName and searching for a visual element (supposedly defined in your XAML visual tree) with Name="This". There's no such thing (apparently, I couldn't tell because you didn't post the complete XAML tree).
If you want to bind a Visual Element Property to another Property in the same element you have you use RelativeSource Self