Access and edit Image inside Button on Button click in code behind - c#

I have a Listview built like this:
<ListView x:Name="listprimi" RelativePanel.Below="primi" ItemsSource="{x:Bind obs_prims2}" HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Tag="{Binding id}" Grid.Column="0" Padding="0" BorderThickness="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="100" Background="White" Click="selectMeal0">
<Image Name="sel0" Width="80" Height="80" Source="Images/ic_uncheck.png" RelativePanel.AlignLeftWithPanel="True" />
</Button>
<Image Width="120" Height="120" Margin="30,0,0,0" Source="{Binding idImg}" RelativePanel.AlignLeftWithPanel="True" />
<TextBlock Text="{Binding descr}" RelativePanel.AlignHorizontalCenterWithPanel="True" Height="100" Name="namemeal" Padding="60,15" Foreground="Gray" FontSize="26.7"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I would like to change the image named "sel0" which is inside the button when the button is clicked. I have set the code behind function selectMeal0 but I have no idea on how to do it.
In addition I would also like to change the images of all the other elements of the list in the same function.
I have tried something like this but it doesn't work.
UPDATE:
This is the class
public class Pasto : INotifyPropertyChanged
{
public string id { get; set; }
public string descr { get; set; }
public ImageSource idImg { get; set; }
private ImageSource imgChecked;
public ImageSource ImgChecked {
get { return imgChecked; }
set
{
if (imgChecked != value)
{
imgChecked = value;
OnPropertyChanged("ImgChecked");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
I've changed the ListView like this:
<ListView x:Name="listprimi" RelativePanel.Below="primi" ItemsSource="{x:Bind obs_prims2}" HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Tag="{Binding id}" Grid.Column="0" Padding="0" BorderThickness="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="100" Background="White" Click="selectMeal0">
<Image Name="sel0" Width="80" Height="80" Source="{Binding ImgChecked}" RelativePanel.AlignLeftWithPanel="True" />
</Button>
<Image Width="120" Height="120" Margin="30,0,0,0" Source="{Binding idImg}" RelativePanel.AlignLeftWithPanel="True" />
<TextBlock Text="{Binding descr}" RelativePanel.AlignHorizontalCenterWithPanel="True" Height="100" Name="namemeal" Padding="60,15" Foreground="Gray" FontSize="26.7"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
so that the image I'd like to edit is ImageChecked in the class.
The function selectMeal should change all the images to "unchecked" and then the selected item's image to "checked".

in selectMeal0, you get access to the sender object, which is a Button in this case. The Button have Content property, which in turn containts the Image. At this point you can do anything to the Image.
BUT
you can also bind the source of the image to a property of your model. And make change to the model to update the image.

In order to change Image inside your DataTemplate of your ListView, you should change the necessary item from your collection on button click:
So, you have a collection obs_prims2 as ItemsSource for ListView. Then you need to take the the one item from your collection obs_prims2. For example:
var item=obs_prims2.FirstOrDefault();
then set new Image address:
item.idImg="C:/1.png";
Moreover, your Model class should implement INotifyPropertyChanged interface to show any changes. For example:
public class Book : INotifyPropertyChanged
{
private string title;
public string Title {
get { return title; }
set {
title = value;
OnPropertyChanged("Title");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(String info)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
Update:
To get an item that you want, then you can get item by index, like that
Person person = Persons[1];. For example:
private void FillDataGridByTypeCollection()
{
Persons.Add(new Person() { Id = 1, Name = "Ben" });
Persons.Add(new Person() { Id = 1, Name = "Joseph" });
Persons.Add(new Person() { Id = 1, Name = "Jon" });
Persons.Add(new Person() { Id = 1, Name = "Magnus Montin" });
Persons.Add(new Person() { Id = 1, Name = "Andy" });
Person person = Persons[1];
}
private ObservableCollection<Person> persons = new ObservableCollection<Person>();
public ObservableCollection<Person> Persons
{
get { return persons; }
set { persons = value; }
}
Model:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}

Related

Dynamic ViewModel's bound values becomes null in WPF

I followed few online tutorials to switch between dynamic views from a ListView.
In my MainWindow, I have a ListView in the left pane with ItemsSource of list of sub ViewModels. (Each sub ViewModel implements an Interface)
Each ViewModel have its own View as a DataTemplate.
I'm calling the GenerateReport() method of the selected view from the MainWindow. But the values of selected views becomes null.
Download my source from Github.
Steps to reproduce the issue:
Run the application and type text in the Students Report's Id and Name. (The breakpoints in StudentReportViewModels's properties are properly hitting and values updated.)
Then click Generate Report button. The StudentReportViewModels's properties values becomes null.
How to fix the issue? Please help.
Source:
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vm:StudentsReportViewModel}">
<view:StudentsReport/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MarksReportViewModel}">
<view:MarksReport />
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding Reports}" SelectedItem="{Binding SelectedReport, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<GridSplitter Grid.Row="1" Grid.Column="1" Width="5" ResizeBehavior="PreviousAndNext" HorizontalAlignment="Stretch"/>
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ContentControl Content="{Binding SelectedReport.ViewModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ScrollViewer>
<Button Content="Generate Report" Grid.Row="2" Margin="5" HorizontalAlignment="Right" Command="{Binding GenerateReportCommand}"/>
</Grid>
</Grid>
MainWindowViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private ICommand _generateReportCommand;
private Report selectedReport;
public Report SelectedReport
{
get
{
return this.selectedReport;
}
set
{
if (value != this.selectedReport)
{
this.selectedReport = value;
NotifyPropertyChanged();
}
}
}
public List<Report> Reports { get; set; }
public ICommand GenerateReportCommand
{
get
{
if (_generateReportCommand == null)
{
_generateReportCommand = new RelayCommand(
p => GenerateReport()
);
}
return _generateReportCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MainWindowViewModel()
{
Reports = new List<Report>
{
new Report{ Name = "Students Report", ViewModel = new StudentsReportViewModel()},
new Report{ Name = "Marks Report", ViewModel = new MarksReportViewModel()}
};
SelectedReport = Reports[0];
}
public void GenerateReport()
{
SelectedReport.ViewModel.GenerateReport();
}
}
StudentsReport.xaml
<TextBox Height="25" Width="100" Margin="5" Text="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Height="25" Width="100" Margin="5" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
StudentsReportViewModel:
public class StudentsReportViewModel : INotifyPropertyChanged, IReportViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string id;
public string Id
{
get
{
return this.id;
}
set
{
if (value != this.id)
{
this.id = value;
NotifyPropertyChanged();
}
}
}
private string name;
public string Name
{
get
{
return this.name;
}
set
{
if (value != this.name)
{
this.name = value;
NotifyPropertyChanged();
}
}
}
public StudentsReportViewModel()
{
}
public void GenerateReport()
{
System.Windows.MessageBox.Show($"Id = {Id}, Name = {Name}");
}
}
Interface:
public interface IReportViewModel
{
void GenerateReport();
}
Model:
public class Report
{
public string Name { get; set; }
public IReportViewModel ViewModel { get; set; }
}
Your StudentsReport.xaml UserControl is binding to an instance of the StudentsReportViewModel created in the XAML:
<UserControl.DataContext>
<vm:StudentsReportViewModel/>
</UserControl.DataContext>
The Generate Report button however is calling into another instance of the StudentsReportViewModel that is create in the MainWindowVieModel constructor and is stored in the Report class.
Reports = new List<Report>
{
new Report{ Name = "Students Report", ViewModel = new StudentsReportViewModel()},
new Report{ Name = "Marks Report", ViewModel = new MarksReportViewModel()}
};
You need to remove one of these instances so that the UserControl's DataContext is bound to the same view model instance that you are generating the report message from.
I suggest deleting this code from StudentsReport.xaml:
<UserControl.DataContext>
<vm:StudentsReportViewModel/>
</UserControl.DataContext>

can listbox show both key and value from dictionary

I have a dictionary, there's a int property in MyClass
public Dictionary<MyClass, BitmapImage> MyList = new Dictionary<MyClass, BitmapImage>();
I want listbox to show images from this collection (like ItemsSource?) and to show each property in textboxes near each image, is that possible?
Code behind example:
C#:
public MainWindow()
{
InitializeComponent();
PopulatePeople();
}
private void PopulatePeople()
{
List<Person> persons = new List<Person>();
for (int i = 0; i < 10; i++)
{
persons.Add(new Person() { Name = "Bob " + i.ToString(), ImageAddress = "Images/peach.jpg" });
}
listBox.ItemsSource = persons;
}
XAML:
<ListBox Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=ImageAddress}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MVVM example:
Download the example from OneDrive.
It is better to use DataTemplate in ListBox to show Image and TextBox.
Model:
public class Person
{
public int IDPerson { get; set; }
public string Name { get; set; }
public string ImageAddress { get; set; }
}
You can fill your collection in constructor of ViewModel:
ViewModel:
public class YourViewModel:INotifyPropertyChanged
{
private ObservableCollection<Person> persons = new ObservableCollection<Person>();
public ObservableCollection<Person> Persons
{
get { return persons; }
set
{
persons = value;
OnPropertyChanged("Persons");
}
}
public YourViewModel()
{
FillThePersons();
}
private void FillThePersons()
{
for (int i = 0; i < 10; i++)
{
persons.Add(new Person() { Name = "Bob " + i.ToString(),
ImageAddress="Images/peach.jpg" });// Images is the name folder in your project
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<ListBox ItemsSource="{Binding Persons}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=ImageAddress}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The following example one image to all items:
<ListBox ItemsSource="{Binding Persons}">
<ListBox.Resources>
<BitmapImage x:Key="AnImage" UriSource="Images/yourImage.png" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource AnImage}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The Result:

WPF ObservableCollection not updating view

this is xaml part
<ItemsControl x:Name="EventsTop">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,1,0,0">
<Button Content="{Binding Name}" Template="{DynamicResource ButtonFirst}" Height="50" Margin="15,0,0,0" Padding="10,0,15,0" FontSize="19" FontFamily="/Resources/Fonts/Font Awesome/#FontAwesome" BorderThickness="5,0,0,0" BorderBrush="#8CC152" Background="#2980B9" HorizontalContentAlignment="Left" Foreground="Black" Click="TabOpen" Tag="{Binding Id}"></Button>
<StackPanel Background="#2980B9" Margin="15,0,0,5" Visibility="Collapsed" AllowDrop="True" Tag="{Binding Id}" Drop="RowDrop">
<Border BorderThickness="5,0,0,0" BorderBrush="#8CC152">
<StackPanel>
<DockPanel LastChildFill="False">
<Label DockPanel.Dock="Left" Width="140" Content="Date" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label DockPanel.Dock="Left" Width="190" Content="Event" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label DockPanel.Dock="Left" Width="100" Content="Select" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
</DockPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="150">
<ItemsControl ItemsSource="{Binding Details}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="False">
<Label Content="{Binding Date}" DockPanel.Dock="Left" Width="140" FontSize="19" BorderThickness="0" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label Content="{Binding EventName}" DockPanel.Dock="Left" Width="165" FontSize="19" BorderThickness="0" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Border Width="97">
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding Checked}"></CheckBox>
</Border>
<Button Width="25" DockPanel.Dock="Left" Content="" BorderThickness="0" Background="Transparent" FontFamily="/Resources/Fonts/Font Awesome/#FontAwesome"></Button>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
this is xaml.cs
private void WindowLoaded(object sender, RoutedEventArgs e)
{
EventHelper eventHelper = new EventHelper();
TopEvents = eventHelper.GetSports(EventHelper.EventGroup.Top);
foreach (Sport item in TopEvents)
{
item.Name = "\uf196 " + item.Name;
}
EventsTop.ItemsSource = TopEvents;
AllEvents = eventHelper.GetSports(EventHelper.EventGroup.All);
foreach (Sport item in AllEvents)
{
item.Name = "\uf196 " + item.Name;
}
EventsAll.ItemsSource = AllEvents;
Sport.ItemsSource = eventHelper.GetSports(EventHelper.EventGroup.All);
}
private void RowMouseDown(object sender, MouseButtonEventArgs e)
{
DockPanel currentRow = (DockPanel) sender;
int rowOffset = Convert.ToInt32(currentRow.Tag);
DragDrop.DoDragDrop(currentRow,rowOffset,DragDropEffects.Copy);
}
private void RowDrop(object sender, DragEventArgs e)
{
int rowOffset = (int) e.Data.GetData(typeof (int));
AllEvents[0].Name = "1";
}
Also my model in collection
class Sport : INotifyPropertyChanged
{
private int _id;
private string _name = string.Empty;
private ObservableCollection<Details> _details = new ObservableCollection<Details>();
public int Id
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Content");
}
}
public ObservableCollection<Details> Details
{
get { return _details; }
set { _details = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
MessageBox.Show(info);
}
}
}
So when I am changing property its throwing MessageBox but not updating GUI.
I xaml.cs I am calling methods GetEvents thats
return ObservableCollection
I want to change Name in Sport which is in ObservableCollaction<Sport> AllEvents
You can see it in RowDrop method in xaml.cs
In debugging I notice that AllEvents[0].Name was changed but view was not updating
UPDATE
Part of ObservabelCollection declaration
public MainPage()
{
InitializeComponent();
AllEvents = new ObservableCollection<Sport>();
TopEvents = new ObservableCollection<Sport>();
EventsTop.ItemsSource = TopEvents;
EventsAll.ItemsSource = AllEvents;
}
private ObservableCollection<Sport> AllEvents;
private ObservableCollection<Sport> TopEvents;
UPDATE SECOND
I caught that when I am using window activated event it is working
I found solution.
So, ObservableCollection is working very well but,It needs to be refreshed
for appearing in view and for it we need to use
CollectionViewSource.GetDefaultView(ObservableCollection).Refresh()
method for it
I think it will help someone
The problem with property named passed to NotifyPropertyChanged method. The name of parameter should be property name. Please change the Name property as
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
Use CallerMemberNameAttribute to avoid having to get the name correct and allowing refactoring:
private void NotifyPropertyChanged([CallerMemberName] string info = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
MessageBox.Show(info);
}
}
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged(); //now no need to specify
}
}
Every property setter should notify property change, so:
public IEnumerable<Details> Details //note IEnumerable, no calling code needs to know its concrete type
{
get { return _details; }
set
{
_details = value;
NotifyPropertyChanged();
}
}
And with an observable range collection you could do this:
private readonly ObservableRangeCollection<Details> _details = new ObservableRangeCollection<Details>();
public IEnumerable<Details> Details
{
get { return _details; }
set { _details.Replace(value); }
}
From MSDN.
Occurs when an item is added, removed, changed, moved, or the entire
list is refreshed.
The changed does not mean when child properties are changed, but when you change the item at any index.
So when you modify a collection item you will need to notify the binding that the property was changed. From your window's viewmodel after you have modified the item in the collection, you would notify that the collection was changed.
NotifyPropertyChanged("AllEvents");

How to display data in a textblock from a listbox of objects using databinding?

This is my XAML:
<Window x:Class="H7_oef1_listBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="517.164" Width="733.955">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="457*"/>
<ColumnDefinition Width="60*"/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding}" HorizontalAlignment="Left" Height="299" Margin="10,10,0,0" VerticalAlignment="Top" Width="128">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Padding="5,0,5,0" Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock HorizontalAlignment="Left" Margin="295,31,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding Path=Name}"/>
<TextBlock HorizontalAlignment="Left" Margin="295,31,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding Path=Street}"/>
</Grid>
</Window>
This is my Person class:
class Person : INotifyPropertyChanged
{
string name;
string street;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
public string Street
{
get { return street; }
set
{
street = value;
OnPropertyChanged();
}
}
public static ObservableCollection<Person> GetPersons()
{
var persons = new ObservableCollection<Person>();
persons.Add(new Person() { Name = "name1", Street = "street1", City = "city1", State = "state1", Zip = "1111", Phone = "1111", Cell = "111" });
persons.Add(new Person() { Name = "name2", Street = "street2", City = "city2", State = "state2", Zip = "2222", Phone = "2222", Cell = "2222" });
return persons;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
Main:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = Person.GetPersons();
}
}
I want that the details are shown in the textblocks next to the listbox using the name that is selected in the listbox.
How can I do this using databinding?
You can bind to TextBlock to a property of ListBox.SelectedItem. Give ListBox some name and use it ElementName binding
<ListBox ItemsSource="{Binding}" ... x:Name="myListBox">
<!-- ... -->
</ListBox>
<TextBlock ... Text="{Binding ElementName=myListBox, Path=SelectedItem.Name}"/>
<TextBlock ... Text="{Binding ElementName=myListBox, Path=SelectedItem.Street}"/>

Failing To Bind Dynamically Generated Buttons & Textboxes in WPF

I was working on dynamic generation of labels, buttons and Textbox in my WPF application. Well I was successful in dynamically creating them but I am facing one major issue in it.
Xaml:
<ListBox x:Name="myViewChannelList" HorizontalAlignment="Stretch" Height="Auto" ItemsSource="{Binding}" Margin="0" VerticalAlignment="Stretch" Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="170" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Path=ChanelName}" Margin="50,20,0,0"></Label>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Path=VoltageText}" Height="25" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
<Button Grid.Column="1" Content="Set" Height="25" Command="{Binding ElementName=myViewChannelList, Path=DataContext.SetCommand}" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" ></Button>
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Model Class:
private string _ChanelName = "";
public String ChanelName
{
get
{
return _ChanelName;
}
set
{
if (value != _ChanelName)
{
_ChanelName = value;
OnPropertyChanged("ChanelName");
}
}
}
// Constructor
public VoltageModel(string ChanelName)
{
this.ChanelName = ChanelName;
}
public override string ToString()
{
return _ChanelName;
}
ViewModel Class:
class ChannelList : ObservableCollection<VoltageModel>, INotifyPropertyChanged
{
private string _VoltageText;
public string VoltageText
{
get { return _VoltageText; }
set
{
_VoltageText = value;
OnPropertyChanged("VoltageText");
}
}
// Method gets called when Set Button Is Clicked
public void SetCommandExecuted()
{
string val = VoltageText;
}
//Notify Property Changed members are present
}
Xaml.cs Class:
ChannelList myChanels = new ChannelList();
public VoltageView() // Constructor
{
InitializeComponent();
myChanels.Add(new VoltageModel("VDD__Main"));
myChanels.Add(new VoltageModel("VDD__IO__AUD"));
myChanels.Add(new VoltageModel("VDD__CODEC__AUD"));
myViewChannelList.DataContext = myChanels;
}
This gives me 3 Labels(Content as above), 3 textboxes and 3 buttons when I run the application.
Now when I enter the value inside the textbox it shows null on button click when I put a breakpoint in SetCommandExecuted(). Most importantly any of the 4 button I click generates the event. I want the first textbox and first button to be in sync(bind), 2nd textbx and 2nd button to be in sync and so on. Basically each control must be in sync with the other control in a row. It should not effect the other rows. Is it possible???
Here is the solution to your question. As general practice you want to avoid all logic, building your data, etc. in the code behind. All the business logic should be in the view model which will make it easier to unit test.
Here is the view
.xaml
<StackPanel>
<ListBox HorizontalAlignment="Stretch"
Height="Auto"
ItemsSource="{Binding VoltageCollection}"
VerticalAlignment="Stretch"
Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Width="100"
Content="{Binding ChannelName}" />
<TextBox Width="100"
Text="{Binding VoltageText}" />
<Button Margin="10,0,0,0"
Content="Set"
Command="{Binding VoltageCommand}"
CommandParameter="{Binding VoltageText}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Here is the code behind
.xaml.cs
private ChannelListViewModel m_voltageViewModel;
public MainWindow()
{
InitializeComponent();
m_voltageViewModel = new ChannelListViewModel();
m_voltageViewModel.Initialize();
DataContext = m_voltageViewModel;
}
Here is the Model: VoltageModel
public class VoltageModel : INotifyPropertyChanged
{
public string ChannelName { get; set; }
private string m_voltageText;
public string VoltageText
{
get { return m_voltageText; }
set
{
m_voltageText = value;
OnPropertyChanged("VoltageText");
}
}
public ICommand VoltageCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the ViewModel: ChannelListViewModel
public class ChannelListViewModel
{
private ICommand m_voltageCommand;
public ChannelListViewModel()
{
m_voltageCommand = new DelegateCommand(x => SetCommandExecute(x));
}
public void Initialize()
{
VoltageCollection = new ObservableCollection<VoltageModel> { new VoltageModel() { ChannelName = "VDD__Main", VoltageText = String.Empty, VoltageCommand = m_voltageCommand },
new VoltageModel() { ChannelName = "VDD__IO__AUD", VoltageText = String.Empty, VoltageCommand = m_voltageCommand },
new VoltageModel() { ChannelName = "VDD__CODEC__AUD", VoltageText = String.Empty, VoltageCommand = m_voltageCommand }};
}
public ObservableCollection<VoltageModel> VoltageCollection { get; set; }
public void SetCommandExecute(object voltageText)
{
Debug.WriteLine(voltageText);
}
}
Finally simple DelegateCommand class DelegateCommand
public class DelegateCommand : ICommand
{
Action<object> m_executeDelegate;
public DelegateCommand(Action<object> executeDelegate)
{
m_executeDelegate = executeDelegate;
}
public void Execute(object parameter)
{
m_executeDelegate(parameter);
}
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
}
i didn't get much into what was wrong since i recognized 2 things that were generally very wrong ,and they might be the problem for unexpected behavior on your part .
the first : your DataTemplate places your control one on top of the other .
fix :
<DataTemplate >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Path=ChanelName}" />
<TextBox Grid.Column="1" Text="{Binding Path=VoltageText}" />
<Button Grid.Column="2" Command="{Binding ElementName=myViewChannelList, Path=DataContext.SetCommand}" />
</Grid>
</DataTemplate>
the second : your Properties are set after PropertyChanged event was risen so they would not be updated until the next time you input a value.
fix :
private T _property;
public T Property
{
get { return _property; }
set
{
_property = value;
OnPropertyChanged("Property");
}
}
make these fixes and edit your post if you still have issues post a comment under my answer.

Categories