Command not executing when clicking a button - wpf

How should the collection be bound to Listview and have the button execute the command to add a single user to the collection.
View Model Code:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public class UserViewModel : ViewModelBase
{
private ObservableCollection<User> _UsersList;
private User _user;
public UserViewModel()
{
_user = new User();
_UsersList = new ObservableCollection<User>();
_UsersList.CollectionChanged += _UsersList_CollectionChanged;
}
private void _UsersList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
}
public ObservableCollection<User> Users
{
get { return _UsersList; }
set
{
_UsersList = value;
OnPropertyChanged("Users");
}
}
public User User
{
get
{
return _user;
}
set
{
_user = value;
OnPropertyChanged("User");
}
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater(this);
return mUpdater;
}
//set
//{
// mUpdater = value;
//}
}
private void Submit()
{
Users.Add(User);
User = new User();
}
private class Updater : ICommand
{
#region ICommand Members
UserViewModel _obj;
public Updater(UserViewModel obj)
{
_obj = obj;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_obj.Submit();
}
}
View Xaml:
<ListView Name="UserGrid"
Grid.Row="1"
Margin="4,178,12,13"
ItemsSource="{Binding Users}" >
<ListView.View>
<GridView x:Name="grdTest">
<GridViewColumn Header="UserId"
DisplayMemberBinding="{Binding UserId}"
Width="50"/>
</GridView>
</ListView.View>
</ListView>
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="Name"
HorizontalAlignment="Center"/>
<TextBox Grid.Row="0"
Grid.Column="1"
Width="100"
HorizontalAlignment="Center"
Text="{Binding User.UserId, Mode=TwoWay}"/>
<Button Content="Update"
Grid.Row="1"
Height="23"
HorizontalAlignment="Left"
Margin="310,40,0,0"
Name="btnUpdate"
VerticalAlignment="Top"
Width="141"
Command="{Binding Path=UpdateCommad}" />

The error is in the binding of the button to the command:
Command="{Binding Path=UpdateCommad}" />
It should be:
Command="{Binding Path=UpdateCommand}" />
To find errors like this always read the Debug Output. In this case it quickly showed:
System.Windows.Data Error: 40 : BindingExpression path error:
'UpdateCommad' property not found on 'object' ''UserViewModel'
To prevent this from happening at all you could use Xaml code completion by setting the design-time DataContext type:
<Window x:Class="TestWpfApplication.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:TestWpfApplication"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525"
d:DataContext="{d:DesignInstance Type=local:UserViewModel}">
After setting this you will have code-completion in Xaml: just start typing the binding path and it will offer valid options.

Related

WPF MVVM and Observablecollect

I have a wpf application and I want to update my listview when I change the value through the UI using Observablecollect. But I don't get what I expect. When I change the value I won't update my list view.
View Code(Xaml)
<UserControl x:Class="DataWatch.View.CompareData"
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:ViewModels="clr-namespace:DataWatch.ViewModel"
mc:Ignorable="d"
d:DesignHeight="340" d:DesignWidth="600">
<UserControl.DataContext>
<ViewModels:CompareViewModel/>
</UserControl.DataContext>
<Grid>
<ListView HorizontalAlignment="Left" Name="comparelistview" VerticalAlignment="Top" Width="600" Height="340" ItemsSource="{Binding DisplayData}">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Key"
DisplayMemberBinding="{Binding Path=Key}" />
<GridViewColumn Width="150" Header="Project Data"
DisplayMemberBinding="{Binding Path=ProjectData}" />
<GridViewColumn Width="150" Header="Import Data"
DisplayMemberBinding="{Binding Path=ImportData}"/>
<GridViewColumn Width="150" Header="State"
DisplayMemberBinding="{Binding Path=State}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
ViewModel
namespace DataWatch.ViewModel
{
public class CompareViewModel
{
private ObservableCollection<CompareDiplayData> _displayData;
public CompareViewModel()
{
_displayData = new ObservableCollection<CompareDiplayData>();
}
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
}
}
Model:
namespace DataWatch.Model
{
public class CompareDiplayData : INotifyPropertyChanged
{
private string _key;
public string Key
{
set
{
_key = value;
this.Changed("Key");
}
get
{
return _key;
}
}
private string _projectData;
public string ProjectData
{
set
{
_projectData = value;
this.Changed("ProjectData");
}
get
{
return _projectData;
}
}
private string _importData;
public string ImportData
{
set
{
_importData = value;
this.Changed("ImportData");
}
get
{
return _importData;
}
}
private string _state;
public string State
{
set
{
_state = value;
this.Changed("State");
}
get
{
return _state;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void Changed(string PropertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
When I change the value in displayData ,but Listview won't update the data.
combobox view control
<UserControl x:Class="DataWatch.View.SelectPanel"
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:ViewModels="clr-namespace:DataWatch.ViewModel"
xmlns:AttachProperty="clr-namespace:DataWatch.AttachedProperty"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="600">
<UserControl.DataContext>
<ViewModels:CompareViewModel/>
</UserControl.DataContext>
<Grid Name="good">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150">
</ColumnDefinition>
<ColumnDefinition Width="300">
</ColumnDefinition>
<ColumnDefinition Width="150">
</ColumnDefinition>
</Grid.ColumnDefinitions>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="0" Grid.Row="0" Margin="15,4,6,4" x:Name="KeyComboBox" VerticalAlignment="Top" Width="120" Text="Choose Key" AttachProperty:SelectionBehavior.SelectionChanged="{Binding SelectKeyCmd}" SelectedItem="{Binding SelectedKey, Mode=TwoWay}" ItemsSource="{Binding KeyComboboxItem}"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="0" Margin="90,4,6,4" Name="compareitemcomBox" VerticalAlignment="Top" Width="120" Text="Compare Item" AttachProperty:SelectionBehavior.SelectionChanged="{Binding SelectKeyCmd}" SelectedItem="{Binding SelectedComparedData, Mode=TwoWay}" ItemsSource="{Binding CompareComboboxItem}"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="2" Grid.Row="0" Margin="15,4,6,4" Name="display" VerticalAlignment="Top" Width="120" Text="Choose State"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
</Grid>
Attach property
public class SelectionBehavior
{
public static DependencyProperty SelectionChangedProperty =
DependencyProperty.RegisterAttached("SelectionChanged",
typeof(ICommand),
typeof(SelectionBehavior),
new UIPropertyMetadata(SelectionBehavior.SelectedItemChanged));
public static void SetSelectionChanged(DependencyObject target, ICommand value)
{
target.SetValue(SelectionBehavior.SelectionChangedProperty, value);
}
private static void SelectedItemChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Selector element = target as Selector;
if (element == null) throw new InvalidOperationException("This behavior can be attached to Selector item only.");
if ((e.NewValue != null) && (e.OldValue == null))
{
element.SelectionChanged += SelectionChanged;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
element.SelectionChanged -= SelectionChanged;
}
}
private static void SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
UIElement element = (UIElement)sender;
ICommand command = (ICommand)element.GetValue(SelectionBehavior.SelectionChangedProperty);
command.Execute(((Selector)sender).SelectedValue);
}
}
viewmodel
public class CompareViewModel
{
private readonly ICommand _selectKeyCmd;
private List<string> _pro_Property;
private List<string> _imp_Property;
private string selectedKey;
private ObservableCollection<string> compareComboboxItem;
private DataTable _dt;
private CompareDiplayData domObject;
private ObservableCollection<CompareDiplayData> _displayData;
public CompareViewModel()
{
_displayData = new ObservableCollection<CompareDiplayData>();
_selectKeyCmd = new RelayCommand(ComboboxChanged, ComboboxIsChanged);
}
private void ReslutData()
{_displayData =null;
if (SelectedKey != null && SelectedComparedData != null)
{
foreach (DataRow pdr in _projectDt.Rows)
{
CompareDiplayData cdd = new CompareDiplayData();
foreach (DataRow idr in _importDt.Rows)
{
if (pdr[SelectedKey].ToString() == idr[SelectedKey].ToString())
{
cdd.Key = pdr[SelectedKey].ToString();
cdd.ProjectData = pdr[SelectedComparedData].ToString();
cdd.ImportData = idr[SelectedComparedData].ToString();
if (pdr[SelectedComparedData].ToString() == idr[SelectedComparedData].ToString())
cdd.State = "Match";
else
cdd.State = "Mismatch";
_displayData.Add(cdd);
}
}
}
}
}
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
}
public ICommand SelectKeyCmd
{
get { return _selectKeyCmd; }
}
private void ComboboxChanged(object obj)
{
ReslutData();
}
private bool ComboboxIsChanged(object obj)
{
return true;
}`}`
You are updating the reference of the Observablecollection here so that needs to be notified as the PropertyChange. In order to fix this, you will have to implement the INotifyPropertyChanged on your ViewModel also and write the setter of the DisplayData and raise property change for it.
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
set{_displayData = value;
this.Changed("DisplayData");
}
and in your ReslutData functin instead of update the variable _displayData, update the Property DisplayData.

WPF Data Templates and Buttons

Note: I am using Visual Studio 2012
I have the following DataTemplate (note: there is a TextBlock and Button)
<DataTemplate DataType="{x:Type ctlDefs:myButton}">
<StackPanel>
<TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>
<Button Content="{Binding LabelText}" Margin="0,0,10,0"
Width="{Binding ButtonWidth}"
Height="35"
Command="{Binding ButtonCommand}"
Visibility="Visible"
/>
</StackPanel>
</DataTemplate>
My screen is dynamically adding controls of myButton to an Items Control
<ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
FlowDirection="LeftToRight"
DockPanel.Dock="Right"
>
The first time I render the view, the Textblock shows up for each button, but the Button itself does not. If I hide the view and then show it again, the button will appear sometimes (if the CommandButtons list does not change, if it changes, it never shows).
After rendering the view, I looked in the Output window and no binding errors.
What am I missing.
I've knocked up a quick application to work with your sample code and didn't seem to have any issues with the view. Below is what it looks like when run.
I've included my code below in case it helps. Note: I didn't add a real ICommand object for brevity and I accidentally named the MyButton class with capital M instead of small m like your example
ViewModelBase.cs
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
this.CommandButtons = new ObservableCollection<MyButton>();
}
public ObservableCollection<MyButton> CommandButtons { get; private set; }
}
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainvm = new MainWindowViewModel();
var window = new MainWindow
{
DataContext = mainvm
};
window.Show();
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 1" });
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 2" });
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 3" });
}
}
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:ctlDefs="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type ctlDefs:MyButton}">
<StackPanel>
<TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>
<Button Content="{Binding LabelText}" Margin="0,0,10,0"
Width="{Binding ButtonWidth}"
Height="35"
Command="{Binding ButtonCommand}"
Visibility="Visible"
/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
FlowDirection="LeftToRight"
DockPanel.Dock="Right"
/>
</Grid>
MyButton.cs
public class MyButton : ViewModelBase
{
private string _labelText;
public string LabelText
{
get { return this._labelText; }
set { this._labelText = value; this.OnPropertyChanged("LabelText"); }
}
private double _buttonWidth;
public double ButtonWidth
{
get { return _buttonWidth; }
set { _buttonWidth = value; this.OnPropertyChanged("ButtonWidth"); }
}
private ICommand _buttonCommand;
public ICommand ButtonCommand
{
get { return _buttonCommand; }
set { _buttonCommand = value; this.OnPropertyChanged("ButtonCommand"); }
}
}

Cant access nested wpf item command by mvvm

Lets say i got a dummy WPF application (MVVM oriented).
My main window contains a custom List i created, and the list contains a custom item.
the item has an image button, and i want the button command to be the command i got in the viewmodel. the viewmodel is binded to the main window.
how can i do it ?
i attached the dummy project (download it here : http://www.2shared.com/file/qmO3E5rx/NestedCommand.html
or here : http://www.multiupload.nl/KCFLSKAIH0),
but if you don't want to download it,
the code goes like this :
MainWindow XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Application="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Application:List x:Name="myList" DataContext="{Binding}" />
</Grid>
MainWindow Code-Behind:
public MainWindow()
{
InitializeComponent();
CharacterViewModel viewModel = new CharacterViewModel();
this.myList.ItemsList.ItemsSource = viewModel.Model.Powers;
}
List XAML:
<UserControl x:Class="WpfApplication2.List"
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:Application="clr-namespace:WpfApplication2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ListView x:Name="ItemsList" ItemsSource="{Binding Path=Name}">
<ListView.ItemTemplate>
<DataTemplate>
<Application:Item x:Name="myItem" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Item XAML:
<UserControl x:Class="WpfApplication2.Item"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="50">
<Grid>
<Button x:Name="ButtonImage" Command="**????????**">
<Button.Template>
<ControlTemplate>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" >
<Image Width="50" Height="50" Source="/WpfApplication2;component/Images/Jellyfish.jpg"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
ViewModel Code :
public class CharacterViewModel : ObjectBase
{
public Character Model { get; private set; }
public DelegateCommand<object> RemoveCommand { get; private set; }
public CharacterViewModel()
: this(Character.Create())
{
}
public CharacterViewModel(Character model)
{
Model = model;
RemoveCommand = new DelegateCommand<object>(RemoveCommand_Execute, RemoveCommand_CanExecute, "Save");
}
void RemoveCommand_Execute(object arg)
{
Model.Powers.Clear();
MessageBox.Show(string.Format("{0} character powers removed.", Model.Name));
}
bool RemoveCommand_CanExecute(object arg)
{
return Model.Name != string.Empty;
}
}
Model Code:
public class Character : ObjectBase
{
string _Name = string.Empty;
ObservableCollection<string> _Powers = new ObservableCollection<string>();
public string Name
{
get { return _Name; }
set
{
if (_Name == value)
return;
_Name = value;
OnPropertyChanged("Name");
}
}
public ObservableCollection<string> Powers
{
get { return _Powers; }
}
public static Character Create()
{
Character hero = new Character()
{
Name = "Superman",
};
hero.Powers.Add("Flight");
hero.Powers.Add("Strength");
hero.Powers.Add("X-Ray Vision");
return hero;
}
}
Framework Code :
public class DelegateCommand<T> : ICommand
{
public DelegateCommand(Action<T> execute) : this(execute, null) { }
public DelegateCommand(Action<T> execute, Predicate<T> canExecute) : this(execute, canExecute, "") { }
public DelegateCommand(Action<T> execute, Predicate<T> canExecute, string label)
{
_Execute = execute;
_CanExecute = canExecute;
Label = label;
}
readonly Action<T> _Execute = null;
readonly Predicate<T> _CanExecute = null;
public string Label { get; set; }
public void Execute(object parameter)
{
_Execute((T)parameter);
}
public bool CanExecute(object parameter)
{
return _CanExecute == null ? true : _CanExecute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
if (_CanExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_CanExecute != null)
CommandManager.RequerySuggested -= value;
}
}
}
public abstract class ObjectBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected internal void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Thanks for helping
The DataContext for the ListItem is the item that it is bound to which is not what you're looking for. You're looking for the DataContext of the UserControl and to get that you'll need to either reference the UserControl explicitly using ElementName or use a RelativeSource binding to explore the visual tree. RelativeSource is probably the best solution and since it references the control itself you'll need to specify in the Path of the binding that you're looking for the RemoveCommand member on the DataContext - something like Path=DataContext.RemoveCommand. See full example below.
XAML:
<Grid DataContext="{Binding}"> <!-- Set the binding for the DataContext of the control and all of its children -->
<ListView ItemsSource="{Binding Path=Model.Powers}">
<ListView.ItemTemplate>
<DataTemplate>
<!-- Use RelativeSource to access the Grid control and then get its DataContext -->
<Button Command="{Binding Path=DataContext.RemoveCommand, RelativeSource={RelativeSource AncestorType=Grid}}">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" >
<Image Width="50" Height="50" Source="/WpfApplication2;component/Images/Jellyfish.jpg"/>
</Border>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

CustomControl DependencyProperty Binding not working correct

I wrote a customcontrol. It is a textbox with a button which opens a OpenFileDialog.
The Text property of the TextBox is bound to my dependency property "FileName". And if the user selects a file via the OpenFileDialog, i set the result to this property.
The TextBox gets the right value through binding.
But now my problem. For my view I'm using a ViewModel. So I have a Binding to my DependencyProperty "FileName" to the property in my ViewModel.
After changing the "FileName" property (changes direct to the textbox or selecting a file via the dialog), the viewmodel property doesn't update.
CustomControl.xaml.cs
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
namespace WpfApplication1.CustomControl
{
/// <summary>
/// Interaction logic for FileSelectorTextBox.xaml
/// </summary>
public partial class FileSelectorTextBox
: UserControl, INotifyPropertyChanged
{
public FileSelectorTextBox()
{
InitializeComponent();
DataContext = this;
}
#region FileName dependency property
public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register(
"FileName",
typeof(string),
typeof(FileSelectorTextBox),
new FrameworkPropertyMetadata(string.Empty,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnFileNamePropertyChanged),
new CoerceValueCallback(OnCoerceFileNameProperty)));
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { /*SetValue(FileNameProperty, value);*/ CoerceFileName(value); }
}
private bool _shouldCoerceFileName;
private string _coercedFileName;
private object _lastBaseValueFromCoercionCallback;
private object _lastOldValueFromPropertyChangedCallback;
private object _lastNewValueFromPropertyChangedCallback;
private object _fileNameLocalValue;
private ValueSource _fileNameValueSource;
private static void OnFileNamePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is FileSelectorTextBox)
{
(d as FileSelectorTextBox).OnFileNamePropertyChanged(e);
}
}
private void OnFileNamePropertyChanged(DependencyPropertyChangedEventArgs e)
{
LastNewValueFromPropertyChangedCallback = e.NewValue;
LastOldValueFromPropertyChangedCallback = e.OldValue;
FileNameValueSource = DependencyPropertyHelper.GetValueSource(this, FileNameProperty);
FileNameLocalValue = this.ReadLocalValue(FileNameProperty);
}
private static object OnCoerceFileNameProperty(DependencyObject d, object baseValue)
{
if (d is FileSelectorTextBox)
{
return (d as FileSelectorTextBox).OnCoerceFileNameProperty(baseValue);
}
else
{
return baseValue;
}
}
private object OnCoerceFileNameProperty(object baseValue)
{
LastBaseValueFromCoercionCallback = baseValue;
return _shouldCoerceFileName ? _coercedFileName : baseValue;
}
internal void CoerceFileName(string fileName)
{
_shouldCoerceFileName = true;
_coercedFileName = fileName;
CoerceValue(FileNameProperty);
_shouldCoerceFileName = false;
}
#endregion FileName dependency property
#region Public Properties
public ValueSource FileNameValueSource
{
get { return _fileNameValueSource; }
private set
{
_fileNameValueSource = value;
OnPropertyChanged("FileNameValueSource");
}
}
public object FileNameLocalValue
{
get { return _fileNameLocalValue; }
set
{
_fileNameLocalValue = value;
OnPropertyChanged("FileNameLocalValue");
}
}
public object LastBaseValueFromCoercionCallback
{
get { return _lastBaseValueFromCoercionCallback; }
set
{
_lastBaseValueFromCoercionCallback = value;
OnPropertyChanged("LastBaseValueFromCoercionCallback");
}
}
public object LastNewValueFromPropertyChangedCallback
{
get { return _lastNewValueFromPropertyChangedCallback; }
set
{
_lastNewValueFromPropertyChangedCallback = value;
OnPropertyChanged("LastNewValueFromPropertyChangedCallback");
}
}
public object LastOldValueFromPropertyChangedCallback
{
get { return _lastOldValueFromPropertyChangedCallback; }
set
{
_lastOldValueFromPropertyChangedCallback = value;
OnPropertyChanged("LastOldValueFromPropertyChangedCallback");
}
}
#endregion FileName dependency property
private void btnBrowse_Click(object sender, RoutedEventArgs e)
{
FileDialog dlg = null;
dlg = new OpenFileDialog();
bool? result = dlg.ShowDialog();
if (result == true)
{
FileName = dlg.FileName;
}
txtFileName.Focus();
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion INotifyPropertyChanged
}
}
CustomControl.xaml
<UserControl x:Class="WpfApplication1.CustomControl.FileSelectorTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="23" d:DesignWidth="300">
<Border BorderBrush="#FF919191"
BorderThickness="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="80" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBox Name="txtFileName"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Grid.Column="0"
Text="{Binding FileName}" />
<Button Name="btnBrowse"
Click="btnBrowse_Click"
HorizontalContentAlignment="Center"
ToolTip="Datei auswählen"
Margin="1,0,0,0"
Width="29"
Padding="1"
Grid.Column="1">
<Image Source="../Resources/viewmag.png"
Width="15"
Height="15" />
</Button>
</Grid>
</Border>
</UserControl>
Using in a view:
<Window x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
xmlns:controls="clr-namespace:WpfApplication1.CustomControl"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Files}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="File name" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<controls:FileSelectorTextBox FileName="{Binding .}" Height="30" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<ListBox ItemsSource="{Binding Files}" Grid.Row="2">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
And the ViewModel:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfApplication1.ViewModels
{
internal class MainViewModel
: INotifyPropertyChanged
{
public MainViewModel()
{
Files = new ObservableCollection<string> { "test1.txt", "test2.txt", "test3.txt", "test4.txt" };
}
#region Properties
private ObservableCollection<string> _files;
public ObservableCollection<string> Files
{
get { return _files; }
set
{
_files = value;
OnPropertyChanged("Files");
}
}
#endregion Properties
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion INotifyPropertyChanged Members
}
}
Is there any wrong using of the dependency property?
Note: The problem only occurs in DataGrid.
You need to set binding Mode to TwoWay, because by default binding works one way, i.e. loading changes from the view model, but not updating it back.
<controls:FileSelectorTextBox FileName="{Binding FileName, Mode=TwoWay}" Height="30" />
Another option is to declare your custom dependency property with BindsTwoWayByDefault flag, like this:
public static readonly DependencyProperty FileNameProperty =
DependencyProperty.Register("FileName",
typeof(string),
typeof(FileSelectorTextBox),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
Also when you change your custom dependency property from inside your control use SetCurrentValue method instead of directly assigning the value using property setter. Because if you assign it directly you will break the binding.
So, instead of:
FileName = dlg.FileName;
Do like this:
SetCurrentValue(FileNameProperty, dlg.FileName);
Change as following:
<TextBox Name="txtFileName"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Grid.Column="0"
Text="{Binding FileName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

Listbox Binding Problem

Please solve my problem, my listbox don't bind, i don't why :(
class TLocation
{
public string name;
}
Main Window:
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication5"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<DataTemplate DataType="{x:Type local:TLocation}" x:Key="myTaskTemplate">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="1" Name="textBlock1" Text="{Binding Path=name, FallbackValue=name}" VerticalAlignment="Top" />
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Height="131" HorizontalAlignment="Left" Margin="29,44,0,0" Name="listBox1" VerticalAlignment="Top" Width="200" ItemTemplate="{StaticResource myTaskTemplate}" />
</Grid>
</Window>
Main Window Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<TLocation> list = new List<TLocation>();
TLocation temp = new TLocation();
temp.name = "hi";
list.Add(temp);
listBox1.ItemsSource = list;
}
}
name is a field, you can only bind to public properties though. You might also want to implement INotifyPropertyChanged if the name changes at runtime. If you are new to databinding make sure to read the overview.
e.g.
public class TLocation : INotifyPropertyChanged
{
private string _name = null;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
(Binding is also case senstitive and your capitalization is not following the conventions so i changed it in my example, here the Binding.Path would need to be Name.)

Resources