I want to Update a ListView from a ModelView, it works only one time (at program start), which is wrong.
The method BatterieApiMqtt_MessageReceived is called every two seconds, the value are changing.
Thanks
Here is my Code
Model
public class AccuModel : INotifyPropertyChanged
{
private string id;
private string voltage;
private string current;
private string power;
private string ladezustand;
private string restkapazität;
[JsonProperty("id")]
public string Id
{
get { return id; }
set
{
id = value;
OnPropertyChanged("Id");
}
}
[JsonProperty("voltage")]
public string Voltage
{
get { return voltage; }
set
{
voltage = value;
OnPropertyChanged("Voltage");
}
}
[JsonProperty("current")]
public string Current
{
get { return current; }
set
{
current = value;
OnPropertyChanged("Current");
}
}
[JsonProperty("power")]
public string Power
{
get { return power; }
set
{
power = value;
OnPropertyChanged("Power");
}
}
[JsonProperty("ladezustand")]
public string Ladezustand
{
get { return ladezustand; }
set
{
ladezustand = value;
OnPropertyChanged("Ladezustand");
}
}
[JsonProperty("restkapazität")]
public string Restkapazität
{
get { return restkapazität; }
set
{
restkapazität = value;
OnPropertyChanged("Restkapazität");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Model
public class EnergieMonitor
{
[JsonProperty("values")]
public IList accuValues { get; set; }
}
ModelView
public class AccuViewModel
{
private IList<AccuModel> _AccuModelsList;
private Api.BatterieApi BatterieApiMqtt;
public AccuViewModel()
{
this.BatterieApiMqtt = new Api.BatterieApi();
BatterieApiMqtt.MessageReceived += BatterieApiMqtt_MessageReceived;
}
private void BatterieApiMqtt_MessageReceived(object sender, EventArgs e)
{
var values = BatterieApiMqtt.EnergieMonitorValues().accuValues;
_AccuModelsList = new List<AccuModel>();
if (_AccuModelsList.Count > 0)
{
_AccuModelsList.Clear();
}
for (int i = 0; i < values.Count; i++)
{
_AccuModelsList.Add(new AccuModel());
_AccuModelsList[i] = values[i];
}
}
public IList<AccuModel> AccuModel
{
get { return _AccuModelsList; }
set { _AccuModelsList = value; }
}
}
Control.cs
public ucWBatterie12VUebersicht()
{
InitializeComponent();
AccuViewModel viewModel = new AccuViewModel();
ListAccus.DataContext = viewModel;
}
Control.xaml
<Grid Margin="10,90,1170,320">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView Name="ListAccus" Grid.Row="1" Margin="10,10,0,10" ItemsSource="{Binding AccuModel}" FontSize="18" FontFamily="Arial" Foreground="WhiteSmoke" Background="Transparent" AlternationCount="2" ItemContainerStyle="{StaticResource alternateColor}">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Background" Value="#FF5B8577"/>
<Setter Property="Height" Value="30"/>
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}" Width="50"/>
<GridViewColumn Header="Spannung [V]" DisplayMemberBinding="{Binding Voltage, Mode=TwoWay}" Width="120" />
<GridViewColumn Header="Strom [A]" DisplayMemberBinding="{Binding Current, Mode=TwoWay}" Width="120" />
<GridViewColumn Header="Leistung [W]" DisplayMemberBinding="{Binding Power, Mode=TwoWay}" Width="120" />
<GridViewColumn Header="Ladezustand [%]" DisplayMemberBinding="{Binding Ladezustand, Mode=TwoWay}" Width="150" />
<GridViewColumn Header="Restkapazität [Ah]" DisplayMemberBinding="{Binding Ladezustand, Mode=TwoWay}" Width="150" />
</GridView>
</ListView.View>
</ListView>
</Grid>
Implement InotifyPropertychanged interface on your Viewmodel
user ObservableCollection instead of IList (as slugster suggested)
Do the updates on your Property (AccumodelList) instead of the backing field (_AccuModelList)
This should fix your issue.
You might consider not to clear the list, just assign new Values (might gain performance...)
Related
I would like to make a MatrixControl in my WPF app. First thing it starts from ListView and defines 'MatrixLine' in the model. ListView ItemsSource was an ObservableCollection. I have a simple complete model that shows what I'm aiming for and a picture with the result I expect for the created model.
I have a problem with how to add the 'MatrixLine' property type of Dictionary, [Keys] as a columns header in the ListView and [Values] (boolean) as a sign 'x' on te ListView. (The picture with the result that follows)
Expected result for my model
Result for my model creating in ViewModel constructor
GitHub Project
https://github.com/Varran/WPF_Multiporownywarki_Baza
Model
public class ColorBase
{
public string Name { get; }
public int Saturation { get; private set; }
public ColorBase(string name, int saturation)
{
this.Name = name;
this.Saturation = saturation;
}
public void ChangeSaturation(int newSaturation)
{
Saturation = newSaturation;
}
public override string ToString()
{
return $"ColorBase: {Saturation.ToString().PadLeft(4, ' ')} - '{Name}'";
}
}
public class MixedPaint
{
public string PaintName { get; }
public List<ColorBase> Ingredients { get; }
public MixedPaint(string name)
{
Ingredients = new List<ColorBase>();
this.PaintName= name;
}
public MixedPaint AddIngredient(ColorBase color)
{
bool added = false;
foreach (var item in Ingredients)
{
if (item.Name == color.Name )
{
item.ChangeSaturation(item.Saturation + color.Saturation);
added = true;
}
}
if (!added)
Ingredients.Add(color);
return this;
}
}
public class MatrixLine
{
public ColorBase ColorIngredient { get; private set; }
public Dictionary<string, bool> Matrix;
public MatrixLine(ColorBase color)
{
Matrix = new Dictionary<string, bool>();
this.ColorIngredient = color;
}
public void AddToMatrix(MixedPaint mixedPaint)
{
string paintName = mixedPaint.PaintName;
bool doesItContainIgredient = mixedPaint.Ingredients.Any(o => (o.Name == ColorIngredient.Name &&
o.Saturation == ColorIngredient.Saturation));
Matrix.Add(paintName, doesItContainIgredient);
}
}
ViewModel
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<MixedPaint> mixedPaints;
public ObservableCollection<MixedPaint> MixedPaints { get { return mixedPaints; } }
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private MixedPaint selectedMixedPaint;
public MixedPaint SelectedMixedPaint {
get { return selectedMixedPaint; }
set { selectedMixedPaint = value;
OnPropertyChanged(nameof(SelectedMixedPaint)); } }
private ObservableCollection<MatrixLine> comparisonMatrix;
public ObservableCollection<MatrixLine> ComparisonMatrix { get { return comparisonMatrix; } }
public ViewModel()
{
ColorBase yellowA = new ColorBase("YellowA", 110);
ColorBase yellowB = new ColorBase("YellowB", 175);
ColorBase blueA = new ColorBase("BlueA", 77);
ColorBase blueB = new ColorBase("BlueB", 135);
ColorBase redA = new ColorBase("RedA", 95);
ColorBase redB = new ColorBase("RedB", 225);
ColorBase whiteA = new ColorBase("WhiteA", 200);
MixedPaint greenA = new MixedPaint("GreenLight")
.AddIngredient(yellowA)
.AddIngredient(blueA);
MixedPaint greenB = new MixedPaint("GreenDark")
.AddIngredient(yellowB)
.AddIngredient(blueB);
MixedPaint orangeA = new MixedPaint("OrangeLight")
.AddIngredient(yellowA)
.AddIngredient(redB)
.AddIngredient(whiteA);
MixedPaint orangeB = new MixedPaint("OrangeDark")
.AddIngredient(yellowB)
.AddIngredient(redB);
MixedPaint violet = new MixedPaint("Violet")
.AddIngredient(redA)
.AddIngredient(blueB);
mixedPaints = new ObservableCollection<MixedPaint>() { greenA, greenB, orangeA, orangeB, violet };
SelectedMixedPaint = greenA;
List<ColorBase> uniqueColorsBase = new List<ColorBase>();
foreach (var item in mixedPaints)
foreach (var item2 in item.Ingredients)
if (!uniqueColorsBase.Contains(item2))
uniqueColorsBase.Add(item2);
uniqueColorsBase = uniqueColorsBase.OrderBy(o => o.Name).ThenBy(o => o.Saturation).ToList();
comparisonMatrix = new ObservableCollection<MatrixLine>();
foreach (var color in uniqueColorsBase)
{
MatrixLine line = new MatrixLine(color);
foreach (var mixed in mixedPaints)
line.AddToMatrix(mixed);
comparisonMatrix.Add(line);
}
}
}
View
<Window x:Class="WPF_multi_próby.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:WPF_multi_próby"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="150"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Name="ListOfMixedPaint"
Grid.Row="0" Grid.Column="0" Orientation="Vertical">
<TextBlock Text="List of MixedPaint:"/>
<ListView ItemsSource="{Binding MixedPaints}" SelectedItem="{Binding SelectedMixedPaint}" Margin="10">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="PaintName: "/>
<TextBlock Text="{Binding PaintName}" Width="120" FontWeight="Bold"/>
<TextBlock Text="IngradientCount: " Margin="0,0,10,0"/>
<TextBlock Text="{Binding Ingredients.Count}" FontWeight="Bold"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<StackPanel Name="BOM"
Grid.Column="1" Grid.Row="0">
<TextBlock Text="Ingredients of selected MixedPaint"/>
<ListView ItemsSource="{Binding SelectedMixedPaint.Ingredients}" Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Header="Color Name" DisplayMemberBinding="{Binding Name}" Width="100"/>
<GridViewColumn Header="Color Saturation" DisplayMemberBinding="{Binding Saturation}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
<StackPanel Name="MultiComparerOfPaints"
Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Orientation="Vertical">
<TextBlock Text="Multicomparer of paints"/>
<ListView ItemsSource="{Binding ComparisonMatrix}" Margin="10" FontFamily="Cascadia Code" >
<ListView.View>
<GridView>
<GridViewColumn Header="Unique ingredient" DisplayMemberBinding="{Binding ColorIngredient}" Width="180"/>
<!-- no idea how binding -->
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>
</Window>
I think this is my solution.
I do not fully understand this solution yet, but I will have to try.
DataMatrix in WPF - codeproject
or
Binding matrix arrays to WPF DataGrid
I have a list view, which further contains textbox in datatemplate having columns Name, Address, Country.
Now this listview's Itemsource is binded to observable collection of a model in my viewmodel class.
I am updating the ObjModel (of typeObservableCollection<Model>) by making name and address empty, on some condition in VM, and i can see the values of name, empty in my ObjModel object in viewmodel class.
But those changes are not reflected in UI (ListView), Am i missing something , How to update the listview.
My View is something like this:
<DataTemplate x:Key="EquipmentItemTemplate">
<Grid
x:Name="ListItem"
Height="40"
ZIndex="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="250" />
<ColumnDefinition Width="250" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}" />
<TextBlock Grid.Column="1" Text="{Binding Path=Address}" />
<TextBlock Grid.Column="2" Text="{Binding Path=Country}" />
</Grid>
</DataTemplate>
<ListView x:Name="MaintenanceElements"
ItemContainerStyle="{StaticResource SequenceListItemStyle}"
ItemTemplate="{StaticResource EquipmentItemTemplate}"
ItemsSource="{Binding EquipmentMaintenanceEntityItemsCollection}"
SelectedItem="{Binding SelectedMaintenanceElement}">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn
Width="200"
local:GridViewSort.PropertyName="Name"
Header="Name" />
<GridViewColumn
Width="250"
local:GridViewSort.PropertyName="Address"
Header="Address" />
<GridViewColumn
Width="250"
local:GridViewSort.PropertyName="Country"
Header="Country" />
</GridView>
</ListView.View>
</ListView>
View Model contains:
public ObservableCollection<Model> ObjModel { get; set; }
Some where on some condition i do
ObjModel[0].Name= string.Empty;
It do not update in ListView because its itemsource is binded to Model object observable collection, how to update ListView from here?
My model is:
public class EquipmentMaintenanceModel : ChangeTracker, INotifyPropertyChanged
{
private string name;
private string address;
private string country;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public string Address
{
get { return this.address; }
set { this.address = value; }
}
public string Country
{
get { return this.country; }
set { this.country = value; }
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In your model, you need to fire PropertyChanged when you set the value of a property. eg:
public string Name
{
get { return this.name; }
set
{
if(this.name != value)
{
this.name = value;
OnPropertyChanged(Name);
}
}
}
I've got a project where there is a ListView and when the User clicks the New button the new Object is added to the bottom of the ListView. I've tried using a Content Style class but that didn't work. I just need something that will scroll to the selected item. Below is my code:
View:
<ListView Margin="103,0,0,10" ScrollViewer.CanContentScroll="True" Height="87" SelectedItem="{Binding SelectedSession, Mode=TwoWay}" ItemsSource="{Binding SessionCollection}">
<ListView.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Session Name" Width="180" DisplayMemberBinding="{Binding SessionName, Mode=TwoWay}" />
<GridViewColumn Header="Operator Name" Width="180" DisplayMemberBinding="{Binding OperatorName, Mode=TwoWay}"/>
<GridViewColumn Header="Session Date" Width="180" DisplayMemberBinding="{Binding SessionDate, Mode=TwoWay}"/>
</GridView>
</ListView.View>
</ListView>
View Model code for the New Button :
public void NewSession()
{
Session newSession = new Session();
SessionCollection.Add(newSession);
SelectedSession = newSession;
SessionDate = DateTime.Now;
StartTime = DateTime.Now;
EndTime = DateTime.Now;
ProjectManager.Instance.CurrentSession = null;
}
public ObservableCollection<Session> SessionCollection
{
get
{
if (currentDatabaseProj.Sessions == null)
{
return currentDatabaseProj.Sessions;
}
else
{
return currentDatabaseProj.Sessions;
}
}
private set
{
currentDatabaseProj.Sessions = value;
IsNewProjClicked = true;
this.RaisePropertyChanged("SessionCollection");
}
}
One simple handler in the code behind should do the trick
(i simplify your code to made it clear)
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView x:Name="listView" Margin="10" ScrollViewer.CanContentScroll="True" SelectedItem="{Binding SelectedSession, Mode=TwoWay}" ItemsSource="{Binding SessionCollection}">
<ListView.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Session Name" Width="180" DisplayMemberBinding="{Binding}" />
</GridView>
</ListView.View>
</ListView>
<Button Click="UIElement_NewElementHandler" Grid.Row="1" Content="Add Item" Command="{Binding AddItem}"></Button>
</Grid>
and in the view's code behind add the following handler :
private void UIElement_NewElementHandler(object sender, RoutedEventArgs routedEventArgs)
{
var border = VisualTreeHelper.GetChild(listView, 0) as Decorator;
var scrollViewer = border.Child as ScrollViewer;
scrollViewer.ScrollToBottom();
}
ps: scrolling down to a new added item is something ViewRelated, none of the View Model Properties are used in the codebehind, so i believe you are not violating any mvvm rule by doing that.
Working example with behavior
xaml
<Window x:Class="ListView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:listView="clr-namespace:ListView"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Content="Add New" Grid.Row="0" Click="Button_Click"></Button>
<ListView Grid.Row="1" ItemsSource="{Binding MySessionList}" SelectedItem="{Binding SelectedSession, Mode=TwoWay}">
<i:Interaction.Behaviors>
<listView:ScrollIntoViewBehavior/>
</i:Interaction.Behaviors>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="200" DisplayMemberBinding="{Binding Name, Mode=TwoWay}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
cs
public partial class MainWindow : Window
{
private Viewmodel _data;
public MainWindow()
{
InitializeComponent();
_data = new Viewmodel();
this.DataContext = _data;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_data.AddNew();
}
}
public class Viewmodel : INotifyPropertyChanged
{
private MySession _selectedSession;
public ObservableCollection<MySession> MySessionList { get; set; }
public Viewmodel()
{
this.MySessionList = new ObservableCollection<MySession>();
//fill with some data
for (int i = 0; i < 20; i++)
{
this.MySessionList.Add(new MySession(){Name = "Index : " + i});
}
}
public MySession SelectedSession
{
get { return _selectedSession; }
set
{
_selectedSession = value;
OnPropertyChanged();
}
}
//should be a Command for MVVM but for quick and dirty
public void AddNew()
{
var toAdd = new MySession() {Name = "New Added " + DateTime.Now.ToLongTimeString()};
this.MySessionList.Add(toAdd);
this.SelectedSession = toAdd;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MySession
{
public string Name { get; set; }
}
//ADD REFERENCE: System.Windows.Interactivity
public class ScrollIntoViewBehavior : Behavior<ListBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
private void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var ctrl = sender as ListBox;
if (ctrl == null)
return;
if (ctrl.SelectedItem != null)
{
ctrl.Dispatcher.BeginInvoke(
DispatcherPriority.Render,
new Action(() =>
{
ctrl.UpdateLayout();
ctrl.ScrollIntoView(ctrl.SelectedItem);
}));
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
}
}
I am new to WPF am and porting an application from VC++ 6.0/MFC to c#/WPF (VS2013). Most of my windows development has been in VC++/MFC. I am trying to stick to the MVVM pattern and am writing a few proof of concept apps to get my feet wet. I am having one sticking point so far.
When my app starts up it will present a tree view of customers and bills. I have that working well using a simple hierarchical data template with each level binding to my local data type (view model). What I want to have happen is when a bill is selected (right now I have a button to press on the bill template) I want the treeview to be replaced by a detail view of the bill (I don't want a dialog to pop up).
The Xaml for this is:
<DockPanel>
<TreeView x:Name="trvGroups" ItemsSource="{Binding LBGroups}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
<TreeView.ItemContainerStyle>
<!--
This Style binds a TreeViewItem to a LBtreeViewItemViewModel
-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:BillViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding BillName}" />
<Button Command="{Binding Path=BillEditCommand}">Edit</Button>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</DockPanel>
Right now I have more questions than anything. Should I define each view as user controls and put them in window.resources? Do I use data templates? I assume I would change the data context to point to the detail bill view model. What is the best way to do this?
My goal, to adhere to MVVM as I understand it, is to have nothing in the code behind (or as little as possible).
I'm looking more for pointers to get me started along the right path as I research. I getting a little befuddled at the moment.
Thanks in advance.
I'll Show you a plain Master Details Scenario where you can choose models in your TreeView and Edit Them.
CS :
public partial class MainWindow : Window , INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private ICommand onEditBillCommand;
public ICommand OnEditBillCommand
{
get
{
if (onEditBillCommand == null)
onEditBillCommand = new RelayCommand<Bill>
(
bill => { CurrentBill = bill; }
);
return onEditBillCommand;
}
}
private Bill currectBill;
public Bill CurrentBill
{
get { return currectBill; }
set
{
currectBill = value;
PropertyChanged(this, new PropertyChangedEventArgs("CurrentBill"));
}
}
public List<Customer> Customers
{
get
{
List<Customer> customers = new List<Customer>();
for (int i = 0; i < 5; i++)
{
customers.Add(CreateMockCustomer(i));
}
return customers;
}
}
private Customer CreateMockCustomer(int g )
{
Customer c = new Customer();
c.Name = "John (" + g + ")" ;
for (int i = 0; i < 3; i++)
{
c.Bills.Add(CreateMockBill());
}
return c;
}
private Bill CreateMockBill()
{
Bill b = new Bill();
b.Price = 55.5;
b.BoughtOnDate = DateTime.Now.Date;
return b;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class Customer : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ObservableCollection<Bill> bills;
public ObservableCollection<Bill> Bills
{
get
{
if (bills == null)
{
bills = new ObservableCollection<Bill>();
}
return bills;
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class Bill : INotifyPropertyChanged
{
private double price;
public double Price
{
get { return price; }
set
{
price = value;
PropertyChanged(this, new PropertyChangedEventArgs("Price"));
}
}
private DateTime boughtOnDate;
public DateTime BoughtOnDate
{
get { return boughtOnDate; }
set
{
boughtOnDate = value;
PropertyChanged(this, new PropertyChangedEventArgs("BoughtOnDate"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public interface IRelayCommand : ICommand
{
void RaiseCanExecuteChanged();
}
public class RelayCommand<T> : IRelayCommand
{
private Predicate<T> _canExecute;
private Action<T> _execute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
private void Execute(T parameter)
{
_execute(parameter);
}
private bool CanExecute(T parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public bool CanExecute(object parameter)
{
return parameter == null ? false : CanExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var temp = Volatile.Read(ref CanExecuteChanged);
if (temp != null)
temp(this, new EventArgs());
}
}
XAML :
<Window>
<Window.Resources>
<HierarchicalDataTemplate x:Key="customerTemplate" DataType="{x:Type local:Customer}" ItemsSource="{Binding Bills}">
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Price}" />
<TextBlock Text="{Binding BoughtOnDate}" Grid.Column="1" />
<Button Content="Edit" Grid.Column="2"
Command="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.OnEditBillCommand}"
CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Name}" FontFamily="Arial" FontSize="16" FontWeight="Bold" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="0.05*"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView ItemsSource="{Binding Customers}" ItemTemplate="{StaticResource customerTemplate}">
</TreeView>
<Grid Grid.Column="2" DataContext="{Binding CurrentBill, Mode=OneWay}" Background="AliceBlue">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Text="{Binding Price, Mode=TwoWay}" Margin="50"/>
<TextBox Text="{Binding BoughtOnDate, Mode=TwoWay}" Grid.Row="1" Margin="50"/>
</Grid>
</Grid>
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.