Control looses focus on UpdateSourceTrigger = PropertyChanged - c#

I have a ListBox with its ItemSource bound to an ObservableCollection. The ListBox has the following (minimalized) ItemTemplate:
<ListBox ItemsSource="{Binding SelectedDirectory.PluginValues}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid Height="29" Margin="5" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Width="Auto"
Text="{Binding Name, Mode=TwoWay
, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Grid.Column="1" Width="Auto"
Text="{Binding Value, Mode=TwoWay
, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The binding option UpdateSourceTrigger=PropertyChanged causes the TextBoxes to loose the focus after each keypress to the surrounding ListBox. When I remove the option the focus is not lost, but the value in the TextBox is not immediately saved to the property. So when I enter a value and then raise a command (eg via save Button) the property is not updated. Only when I click somewhere else first and then raise the command the value is updated.
Edit
Simplified ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private FbiDirectory selectedDirectory;
public FbiDirectory SelectedDirectory
{
get
{
return this.selectedDirectory;
}
set
{
this.selectedDirectory = value;
this.OnPropertyChanged("SelectedDirectory");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
FbiDirectory class (has nothing to do with the Federal Bureau of investigation):
public class FbiDirectory : INotifyPropertyChanged
{
private ObservableCollection<PluginValue> pluginValues = new ObservableCollection<PluginValue>();
public ObservableCollection<PluginValue> PluginValues
{
get
{
return this.pluginValues;
}
set
{
this.pluginValues = value;
this.OnPropertyChanged("PluginValues");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
PluginValue class:
public class PluginValue : INotifyPropertyChanged
{
private string name;
private string value;
public string Name
{
get => name;
set
{
name = value;
this.OnPropertyChanged("Name");
}
}
public string Value
{
get => value;
set
{
this.value = value;
this.OnPropertyChanged("Value");
}
}
public PluginValue(string name, string value)
{
this.Name = name;
this.Value = value;
}
public PluginValue()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}

A simplified code for your problem may look like this:
public class MyViewModel
{
public ObservableCollection<ItemViewModel> Items { get; set; }
public ICommand SaveCommand { get; }
public MyViewModel()
{
SaveCommand = new RelayCommand(OnSaveCommand);
Items = new ObservableCollection<ItemViewModel>();
Items.Add(new ItemViewModel{Name = "test1", Value = "test1"});
Items.Add(new ItemViewModel{Name = "test2", Value = "test2"});
}
private void OnSaveCommand()
{
var message = Items.Aggregate(new StringBuilder(),
(builder, item) => builder.AppendLine($"{item.Name} {item.Value}"));
message.AppendLine("Will be save");
MessageBox.Show(message.ToString());
}
}
public class ItemViewModel : NotifiableObject
{
private string _value;
private string _name;
public string Name
{
get => _name;
set
{
OnPropertyChanged();
_name = value;
}
}
public string Value
{
get => _value;
set
{
OnPropertyChanged();
_value = value;
}
}
}
public class NotifiableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
With this view:
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Items}" Grid.Row="0">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid Height="29" Margin="5" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Grid.Column="1" Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="Save" Command="{Binding SaveCommand}"></Button>
</Grid>
Not really sure of what's wrong in your code but:
You should use a NotifableObject for the common RaisePropertyChanged behavior
I don't really understand why you use a FbiDirectory instead of directly put the pluginValues into your ViewModel?
Hope it helps.

Related

Navigate to another tab item by a button click from the tab having multiple view models doesn't work

I have to navigate to another tab by a button click from the first tab in a WPF MVVM Application(c#).
I am trying to achieve this by adding binding to Selected Index property in tab control.There are two different view models are used in the first tab.After adding binding to Selected Index property in tab control it loses the rest of view model's access and No data is present on the text boxes in the first tab. Also navigation is not working . how can I use tab navigation if the window has multiple view models. please see sample code.
XAML file
MainWindow.xaml
<Grid>
<TabControl SelectedIndex="{Binding SelectedTab,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
DataContext="{Binding processVM}">
<TabItem Header="Home">
<Grid ShowGridLines="false" >
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<TextBox Name="txtCustomerName"
Grid.Row="0" Grid.Column="1"
Text="{Binding CustomerName}"
DataContext="{Binding customerVM}"></TextBox>
<TextBox Name="txtDepositAmount"
Grid.Row="1" Grid.Column="1"
Text="{Binding DepositAmount}"
DataContext="{Binding customerVM}"></TextBox>
<Button Content="Click" Width="100" Height="50"
Grid.Row="2"
DataContext="{Binding processVM}"
Command="{Binding ButtonCommand}"
/>
</Grid>
Code behind
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel()
{
processVM = new ProcessViewModel(),
customerVM = new CustomerViewModel()
};
}
}
View Models
MainWindowViewModel.cs
class MainWindowViewModel
{
public ProcessViewModel processVM { get; set; }
public CustomerViewModel customerVM { get; set; }
}
ProcessViewModel.cs
public class ProcessViewModel: INotifyPropertyChanged
{
private string depositAmount;
public string DepositAmount
{
get { return depositAmount; }
set {
depositAmount = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("DepositAmount"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private ICommand m_ButtonCommand;
public ICommand ButtonCommand
{
get
{
return m_ButtonCommand;
}
set
{
m_ButtonCommand = value;
}
}
private int selectedTab;
public int SelectedTab
{
get { return selectedTab; }
set
{
selectedTab = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTab"));
}
}
public ProcessViewModel()
{
ButtonCommand = new RelayCommand(new Action<object>(clickbutton));
depositAmount = "450";
}
public void clickbutton(object obj)
{
MessageBox.Show("clicked");
SelectedTab = 1;
}
}
CustomerViewModel.cs
class CustomerViewModel: ProcessViewModel, INotifyPropertyChanged
{
private string customerName;
public string CustomerName
{
get { return customerName; }
set { customerName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CustomerName"));
}
}
public CustomerViewModel()
{
CustomerName = "Alex";
}
public event PropertyChangedEventHandler PropertyChanged;
}
Before adding binding for selected index there were no issues.
Your problem is that you're setting TabControl.DataContext. DataContext is inherited, so all the controls inside it are now using processVM as their binding source instead of MainWindowViewModel.
Instead of setting TabControl.DataContext, change the SelectedIndex binding to this:
SelectedIndex="{Binding processVM.SelectedTab, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Simple DataBinding in wpf application

Hi I am trying to study simple data binding in wpf. I tried and am not succeeding.. please suggest ways..
<Window x:Class="WpfTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="250" Width="350">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Label Content="First Name" Grid.Row="0" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>
<Label Content="Last Name" Grid.Row="1" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>
<Label Content="Full Name" Grid.Row="2" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>
<TextBox x:Name="fName" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" Grid.Row="0" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"/>
<TextBox x:Name="lName" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"/>
<Label x:Name="lblFullName" Content="{Binding FirstName}" Grid.Row="2" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"/>
<Button x:Name="cmdCommand" Content="Command" Grid.Row="3" Grid.Column="1" Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center" Click="cmdCommand_Click"/>
</Grid>
</Window>
Now you see, I want that the label lblFullName to be automatically updated as soon as I type name in textboxes.
Now the my codebehind file looks like this:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies objects registered to receive this event that a property value has changed.
/// </summary>
/// <param name="propertyName">The name of the property that was changed.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public MainWindow()
{
InitializeComponent();
}
private void cmdCommand_Click(object sender, RoutedEventArgs e)
{
lblFullName.Content = FirstName + " " + LastName;
}
}
needless to say, even clicking the command button is not working...
any suggestions?
Setting the DataContext to point to the window itself will solve the immediate problem:
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
The paths in the Binding expressions in the Xaml are all relative to the DataContext of the controls so the DataContext has to be set.
However, I strongly suggest that you put the first name and last name in a separate class (e.g. PersonViewModel) That will make the code easier to read and to maintain.
public class PersonViewModel : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new PersonViewModel();
}
private void cmdCommand_Click(object sender, RoutedEventArgs e)
{
lblFullName.Content = FirstName + " " + LastName;
}
}
Now the last part of this code does not work anymore. There are two solutions.
The quick and dirty solution:
private void cmdCommand_Click(object sender, RoutedEventArgs e)
{
var person = this.DataContext as PersonViewModel;
if(person == null) return;
lblFullName.Content = string.Format("{0} {1}", person.FirstName, person.LastName);
}
A better way, when trying to do proper MVVM is to put an extra property in the ViewModel (note the change in the First and Last name properties!):
public class PersonViewModel : INotifyPropertyChanged
{
public string FullName
{
get
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("FullName");
}
}
}
// remainder of the class remains the same
}
Remove the button and its click handler. Bind the label to this new property:
<Label Content="{Binding FullName}"
Grid.Row="2" Grid.Column="1"
Height="25" HorizontalAlignment="Stretch"/>

ObservableCollection binding on condition

I have Observable collection with MarketPrices , This observable collection I have bind to an ItemsControl as Below .
1) Now I don't want to show all Items in Observable Collection , want to show only items that user click Add and selected Pair (GBPJPY, USDGBP..) needs to show in Items Control.
2) If user changed item in Comobobox from GBPJPY to USDGBP , then the price( DataTemplate) of GBPJPY need to update USDGBP.
How can I achieve both conditions. Please note that below code doesn't have real-time update but in project I have relatime price update as well, so observable collection updates on price changes.
Code So Far
public class PriceModel : INotifyPropertyChanged
{
private double _askPrice;
private double _offerPrice;
private string _selectedPair;
public PriceModel()
{
Pairs = new ObservableCollection<string> {"GBPUSD", "GBPEUR", "USDGBP", "GBPJPY"};
}
public double AskPrice
{
get { return _askPrice; }
set
{
_askPrice = value;
OnPropertyChanged("AskPrice");
}
}
public double OfferPrice
{
get { return _offerPrice; }
set
{
_offerPrice = value;
OnPropertyChanged("OfferPrice");
}
}
public string SelectedPair
{
get { return _selectedPair; }
set
{
_selectedPair = value;
OnPropertyChanged(SelectedPair);
}
}
public ObservableCollection<string> Pairs { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
MarketPrices = new ObservableCollection<PriceModel>
{
new PriceModel {AskPrice = 1.60345, OfferPrice = 1.60335, SelectedPair = "GBPUSD"},
new PriceModel {AskPrice = 1.71345, OfferPrice = 1.71335, SelectedPair = "GBPEUR"},
new PriceModel {AskPrice = 1.23345, OfferPrice = 1.23335, SelectedPair = "USDGBP"},
new PriceModel {AskPrice = 1.34345, OfferPrice = 1.34335, SelectedPair = "GBPJPY"}
};
}
public ObservableCollection<PriceModel> MarketPrices { get; set; }
}
XAML
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding MarketPrices}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="5" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel AllowDrop="True" ClipToBounds="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox ItemsSource="{Binding Pairs}" SelectedItem="{Binding SelectedPair}" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBlock Margin="2" Text="Ask Price" />
<TextBlock Margin="2" Text="{Binding AskPrice}" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Margin="2" Text="Offer Price" />
<TextBlock Margin="2" Text="{Binding OfferPrice}" />
</StackPanel>
</Grid>
</Grid>
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
If I understand your question correctly, you want to display a list of pairs in a ComboBox and the details for a Selected pair only but not for all pairs?
If that's the case, there are couple problems with the code.
PriceModel
You do not need a collection of all available pairs in your PriceModel class. Also, You do not need SelectedPair property in this class, may be your intention was to indicate the name of the pair, you could update your PriceModel to:
public class PriceModel : INotifyPropertyChanged
{
private double _askPrice;
private double _offerPrice;
private string _pair;
public PriceModel(string pair)
{
_pair = pair;
}
public string Pair
{
get { return _pair; }
}
public double AskPrice
{
get { return _askPrice; }
set
{
_askPrice = value;
OnPropertyChanged("AskPrice");
}
}
public double OfferPrice
{
get { return _offerPrice; }
set
{
_offerPrice = value;
OnPropertyChanged("OfferPrice");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml.cs
You have a property named MarketPrices to hold the collection of Pairs, but no property to hold the Selected pair. Add a property named SelectedPair of type PriceModel. The Updated code would like this:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private PriceModel _selectedPair;
public MainWindow()
{
InitializeComponent();
DataContext = this;
MarketPrices = new ObservableCollection<PriceModel>
{
new PriceModel("GBPUSD") {AskPrice = 1.60345, OfferPrice = 1.60335, },
new PriceModel("GBPEUR") {AskPrice = 1.71345, OfferPrice = 1.71335, },
new PriceModel("USDGBP") {AskPrice = 1.23345, OfferPrice = 1.23335, },
new PriceModel("GBPJPY") {AskPrice = 1.34345, OfferPrice = 1.34335, }
};
}
public ObservableCollection<PriceModel> MarketPrices { get; set; }
public PriceModel SelectedPair
{
get { return _selectedPair; }
set
{
_selectedPair = value;
OnPropertyChanged("SelectedPair");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml
You could just use ComboBox to display the list of available pairs and update your bindings for TextBoxes to refer SelectedPair.
Update XAML would look like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox ItemsSource="{Binding Pairs}" SelectedItem="{Binding SelectedPair}" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBlock Margin="2" Text="Ask Price" />
<TextBlock Margin="2" Text="{Binding SelectedPair.AskPrice}" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Margin="2" Text="Offer Price" />
<TextBlock Margin="2" Text="{Binding SelectedPair.OfferPrice}" />
</StackPanel>
</Grid>
</Grid>
Sample Output

Cant connect info to listbox whis is in one more listbox in WP7

I have a class News
public class News : ObservableCollection<New>
{
public News()
: base()
{
}
}
A class New
public class New : INotifyPropertyChanged
{
public PhotoAttachments Photo
{
get
{
return photoAttachments;
}
set
{
photoAttachments = value;
OnPropertyChanged("Photo");
}
}
// some fields such as date, text, id, sourceName etc
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{//realisation of method}
public PhotoAttachments photoAttachments = new PhotoAttachments(); // it is a collection, such as News, but it contains objects of class PhotoAttachment, which have property with string url to photo
}
after
InitializeComponent(); i write this.listBox.ItemsSource = NewsList;
so a have a listbox with objects of class New.
In these listbox I created another one listbox, and tried to fill it by PhotoAttachments collection. And here I have a problem, listbox with photos don't show photos(but they exists). Here is XAML:
// I can select different <local:NewsTemplateSelector.Photos>
//style of listbox <DataTemplate>
//using NewsTemplateSelector <Border BorderBrush="Red" BorderThickness="2" Width="400" Height="300" Margin="10">
<StackPanel Orientation="Horizontal" Width="400" Height="300">
<Image Source="{Binding SourceImage}" Height="75" Width="75" Margin="0,-225,0,0" />
<Canvas Width="400">
<TextBlock Text="{Binding SourceName}" Foreground="Black" FontSize="25" TextWrapping="Wrap" Height="65" Width="326" d:LayoutOverrides="VerticalAlignment, Height" />
<ListBox Name="photoListbox" ItemsSource="{Binding Photo}" Height="229" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="326" Canvas.Top="69">
<Image Source="{Binding Big}" Height="200" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="400" />
</ListBox>
</Canvas>
</StackPanel>
</Border>
</DataTemplate>
</local:NewsTemplateSelector.Photos>
PhotoAttachment class:
public class PhotoAttachment : INotifyPropertyChanged
{
private string ownerId;
public string OwnerId { get { return ownerId; } set { ownerId = value; OnPropertyChanged("OwnerId"); } }
private string small;
public string Small { get { return small; } set { small = value; OnPropertyChanged("Small"); } }
private string big;
public string Big { get { return big; } set { big = value; OnPropertyChanged("Big"); } }
public PhotoAttachment(string ownId, string small, string big)
{
ownerId = ownId;
this.small = small;
this.big = big;
}
public PhotoAttachment() { }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Just realized that your XAML for photoListView is missing an ItemTemplate
Something along these lines should do the trick:
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Big}" Height="200" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="400" />
</DataTemplate>
</ListBox.ItemTemplate>

MVVM viewmodel events (commands?)

I have a MVVM setup that creates a View on my MainWindow. I am not sure how to know when a user Clicks on a specific Notification Item inside the View. Where would I add the event, or a command to know when that happens?
here are is my MVVM code :
MainWindow
cs:
NotificationViewModel notificationViewModel = new NotificationViewModel();
notificationViewModel.AddNoticiation(new NotificationModel() { Message = "Error", Name = "Station 21" });
NotificationView.DataContext = notificationViewModel;
xaml:
<notification:NotificationView x:Name="NotificationView" />
NotificationModel
public class NotificationModel : INotifyPropertyChanged
{
private string _Message;
public string Message
{
get { return _Message; }
set
{
if (_Message != value)
{
_Message = value;
RaisePropertyChanged("Message");
}
}
}
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (_Name != value)
{
_Name = value;
RaisePropertyChanged("Name");
}
}
}
public string TimeStamp
{
get { return DateTime.Now.ToString("h:mm:ss"); }
}
#region PropertChanged Block
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
#endregion
}
NotificationViewModel
public class NotificationViewModel
{
private ObservableCollection<NotificationModel> _Notifications = new ObservableCollection<NotificationModel>();
public ObservableCollection<NotificationModel> Notifications
{
get { return _Notifications; }
set { _Notifications = value; }
}
public void AddNoticiation(NotificationModel notification)
{
this.Notifications.Insert(0, notification);
}
}
NotificationView
<Grid>
<StackPanel HorizontalAlignment="Left" >
<ItemsControl ItemsSource="{Binding Path=Notifications}"
Padding="5,5,5,5">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="SlateGray"
CornerRadius="4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Path=TimeStamp}" />
<TextBlock Grid.Column="1"
Text="{Binding Path=Name}" />
<TextBlock Grid.Column="2"
Text="{Binding Path=Message}" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
There's no real selection mechanism built into an ItemsControl. It would probably solve your problem to switch out your ItemsControl for a ListBox.
If you do that, you can bind to SelectedItem, then handle any changes made to SelectedItem using the PropertyChanged event.
Example:
In your view model's constructor:
PropertyChanged += NotificationViewModel_PropertyChanged;
Add a property to your view model to allow the binding:
private string _selectedNotification;
public string SelectedNotification
{
get { return _selectedNotification; }
set
{
if (_selectedNotification != value)
{
_selectedNotification = value;
RaisePropertyChanged("SelectedNotification");
}
}
}
Finally, add the event handler to your view model:
NotificationViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e))
{
if (e.PropertyName = "SelectedNotification") DoStuff();
}
You may find that you don't even need to hook into PropertyChanged if you just want to update another control in your view based on the selected item in your list box. You can just bind directly to the property within xaml.

Categories

Resources