Datagrid don't show elements MVVM - wpf

I am trying to load a list in my datagrid when pressing the button but it does not show anything.
Do you know where the error is?
Thanks a lot
<UserControl x:Class="HiberusHubClient.View.RatesView"
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:local="clr-namespace:HiberusHubClient.View"
mc:Ignorable="d"
x:Name="RatesViewControl"
d:DesignHeight="450" d:DesignWidth="800"
DataContext="RatesViewControlDataContext">
<Grid>
<StackPanel HorizontalAlignment = "Left">
<Button x:Name="btnUpdate" Width="100" Height="20" HorizontalAlignment="Center" Grid.Row="1" Content="Update" Command="{Binding Path=UpdateCommand}" />
<StackPanel Orientation = "Horizontal">
<DataGrid Width="200" Height="400" AutoGenerateColumns="True" ItemsSource="{Binding LstConvertRates,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="lstDataGrid" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
public partial class RatesView : UserControl
{
public RatesView()
{
InitializeComponent();
DataContext = new ConvertRatesViewModel();
}
}
ConvertRate.cs
public class ConvertRate
{
}
public class ConvertRates : INotifyPropertyChanged
{
private string currencyFrom;
private string currencyTo;
private string rate;
public string CurrencyFrom
{
get
{
return currencyFrom;
}
set
{
if (currencyFrom != value)
{
currencyFrom = value;
RaisePropertyChanged("CurrencyFrom");
}
}
}
public string CurrencyTo
{
get { return currencyTo; }
set
{
if (currencyTo != value)
{
currencyTo = value;
RaisePropertyChanged("CurrencyTo");
}
}
}
public string Rate
{
get { return rate; }
set
{
if (rate != value)
{
rate = value;
RaisePropertyChanged("Rate");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
ConvertRatesViewModel .cs
internal class ConvertRatesViewModel : INotifyPropertyChanged
{
private ObservableCollection<ConvertRates> lstConvertRates;
public ObservableCollection<ConvertRates> LstConvertRates
{
get
{
return lstConvertRates;
}
set
{
lstConvertRates = value;
//if u want Observable Collection to get updated on edit either
RaisePropertyChanged("LstConvertRates");
}
}
public void getConvertRates()
{
HiberusHubeReference.RequestHubeRestService hiberusHubeReference = new HiberusHubeReference.RequestHubeRestService();
JavaScriptSerializer json = new JavaScriptSerializer();
string aa = json.Serialize(hiberusHubeReference.RequestRates());
ObservableCollection<ConvertRates> oMycustomclassname = Newtonsoft.Json.JsonConvert.DeserializeObject<ObservableCollection<ConvertRates>>(aa);
LstConvertRates = oMycustomclassname;
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater();
return mUpdater;
}
set
{
mUpdater = value;
}
}
}
internal class Updater : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
ConvertRatesViewModel convertRatesViewModel = new ConvertRatesViewModel();
convertRatesViewModel.getConvertRates();
}
}

If you set DataContext in code behind then there is no need to set DataContext also in xaml. You can remove DataContext="RatesViewControlDataContext" from RatesView.xaml. Btw. DataContext="RatesViewControlDataContext" means DataContext property is set to string "RatesViewControlDataContext". You probably don't this.
Try to use existing implementation of ICommand interface like RelayCommand.
public class RelayCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
For your UpdateCommand you can modify the setter and create a new instance of RelayCommand. In the constructor you can specify whether the command should be executed and which method on your ConvertRatesViewModel will be called when you press btnUpdate button.
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new RelayCommand(x => true, x => getConvertRates());
return mUpdater;
}
set
{
mUpdater = value;
}
}

Related

WPF TextBlock binding to a ViewModel using TextBlock extension not working

I am binding a view model to a TextBlock. The Inlines property is not a DependencyProperty so I made a TextBlockExtensions class and created an attached DependencyProperty called BindableInlines.
Here below is my View Model.
public class MainWindowModel: INotifyPropertyChanged
{
private buttonAddNewTextCommand _btnAddNewTextCommand;
public ObservableCollection<Inline> ProcessTrackerInlines { get; set; }
public ICommand btnAddNewTextCommand
{
get
{
return _btnAddNewTextCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MainWindowModel()
{
_btnAddNewTextCommand = new buttonAddNewTextCommand(this);
loadProcessTracker();
}
public void addErrorLine(String errorMessage)
{
addText(errorMessage, Brushes.Red, true);
}
public void addNewTextLine()
{
String message = Guid.NewGuid().ToString();
var rand = new Random();
byte[] brushes = new byte[4];
rand.NextBytes(brushes);
ProcessTrackerInlines.Add(new LineBreak());
ProcessTrackerInlines.Add(addText(message, new SolidColorBrush(Color.FromArgb(brushes[0], brushes[1], brushes[2], brushes[3])), true));
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ProcessTrackerInlines"));
}
}
private void loadProcessTracker()
{
ProcessTrackerInlines = new ObservableCollection<Inline>();
var rand = new Random();
byte[] brushes = new byte[4];
for(int i=0; i<5; i++)
{
rand.NextBytes(brushes);
ProcessTrackerInlines.Add(addText($"{Guid.NewGuid().ToString()}\r\n", new SolidColorBrush(Color.FromArgb(brushes[0], brushes[1], brushes[2], brushes[3])), true));
}
}
private Run addText(String textToAdd, Brush foreground = null, Boolean? addTime = null)
{
if (addTime ?? false)
{
textToAdd = addCurrentTime(textToAdd);
}
if (foreground != null)
{
return new Run(textToAdd) { Foreground = foreground };
}
else
{
return new Run(textToAdd);
}
}
private String addCurrentTime(String prefixText)
{
return $"[{DateTime.Now.ToString("h:mm ss tt")}] {prefixText}";
}
}
public class buttonAddNewTextCommand : ICommand
{
private MainWindowModel owner;
public buttonAddNewTextCommand(MainWindowModel _object)
{
owner = _object;
}
public Boolean CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
owner.addNewTextLine();
}
public event EventHandler CanExecuteChanged;
}
My TextBlockExtensions class:
public class TextBlockExtensions
{
public static IEnumerable<Inline> GetBindableInlines(DependencyObject obj)
{
return (IEnumerable<Inline>)obj.GetValue(BindableInlinesProperty);
}
public static void SetBindableInlines(DependencyObject obj, IEnumerable<Inline> value)
{
obj.SetValue(BindableInlinesProperty, value);
}
public static readonly DependencyProperty BindableInlinesProperty =
DependencyProperty.RegisterAttached("BindableInlines", typeof(IEnumerable<Inline>), typeof(TextBlockExtensions), new PropertyMetadata(null, OnBindableInlinesChanged));
private static void OnBindableInlinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var Target = d as TextBlock;
if (Target != null)
{
Target.Inlines.Clear();
Target.Inlines.AddRange((System.Collections.IEnumerable)e.NewValue);
}
}
}
And my XAML:
<Window x:Class="MVVMTextBlock.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:MVVMTextBlock"
xmlns:viewModels="clr-namespace:MVVMTextBlock.ViewModels"
xmlns:extensions="clr-namespace:MVVMTextBlock"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<viewModels:MainWindowModel/>
</Window.DataContext>
<Grid>
<TextBlock extensions:TextBlockExtensions.BindableInlines="{Binding ProcessTrackerInlines, Mode=OneWay}" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Width="578" Margin="25,0,0,0" Height="398"/>
<Button Content="Add New Text" HorizontalAlignment="Left" VerticalAlignment="Top" Width="123" Margin="638,10,0,0" Command="{Binding btnAddNewTextCommand, Mode=OneWay}"/>
</Grid>
</Window>
When the button is clicked the MainWindowModel.addNewTextLine() method is getting hit but the TextBlockExtensions.OnBindableInlinesChanged() is not. So the TextBlock is not getting updated.
I have already changed the BindableInlinesProperty to be an ObservableCollection but it didn't work.
What I am doing wrong?

SelectedItem on ComboBox

There is a ComboBox in the application which is bound to a collection of items. There are cases that user can select an item from the ComboBox but the selected item might not be ready yet so the ComboBox selected item must get back to the previous selected item (or some other item in the collection), but in the current application ComboBox always shows the selected item from the user instead of retrieving the valid item after setting it back and calling notify property change.
The flowing is a simplified code of which shows the problem.
public partial class MainWindow : Window, INotifyPropertyChanged
{
private List<Customer> _Customers = new List<Customer>();
public List<string> CustomerNames
{
get
{
var list = new List<string>();
foreach (var c in _Customers)
{
list.Add(c.Name);
}
return list; ;
}
}
public string CustomerName
{
get
{
var customer = _Customers.Where(c => c.IsReady).FirstOrDefault();
return customer.Name;
}
set
{
NotifyPropertyChanged("CustomerName");
}
}
public MainWindow()
{
SetupCustomers();
InitializeComponent();
this.DataContext = this;
}
private void SetupCustomers()
{
_Customers.Add(new Customer("c1", true));
_Customers.Add(new Customer("c2", false));
_Customers.Add(new Customer("c3", false));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Customer
{
public Customer(string name, bool isReady)
{
this.Name = name;
this.IsReady = isReady;
}
public bool IsReady { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
<Window x:Class="TryComboboxReset.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox Width="100"
Height="25"
ItemsSource="{Binding Path=CustomerNames, Mode=OneWay}"
SelectedItem="{Binding Path=CustomerName, Mode=TwoWay}"/>
</Grid>
The problem was UI thread, I used dispatcher to fix this problem
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<Customer> _Customers =
new ObservableCollection<Customer>();
public ObservableCollection<Customer> CustomerNames
{
get
{
return _Customers;
}
}
public Customer CustomerName
{
get
{
return _Customers.Where(c => c.IsReady == true).FirstOrDefault();
}
set
{
// Delay the revert
Application.Current.Dispatcher.BeginInvoke(
new Action(() => NotifyPropertyChanged("CustomerName")), DispatcherPriority.ContextIdle, null);
}
}
public MainWindow()
{
SetupCustomers();
InitializeComponent();
this.DataContext = this;
}
private void SetupCustomers()
{
_Customers.Add(new Customer("c1", true));
_Customers.Add(new Customer("c2", false));
_Customers.Add(new Customer("c3", false));
CustomerName = _Customers.Where(c => c.IsReady == true).FirstOrDefault();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Customer
{
public Customer(string name, bool isReady)
{
this.Name = name;
this.IsReady = isReady;
}
public bool IsReady { get; set; }
public string Name { get; set; }
}
<ComboBox Width="400"
Height="25"
ItemsSource="{Binding Path=CustomerNames}"
SelectedValue="{Binding CustomerName,Mode=TwoWay}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You aren't actually setting the value of the selected customer name in your setter, and your getter is always going to return the first "ready" customer name it finds...
If I'm understanding the problem correctly, you need to be doing something more along these lines:
private string _customerName = null;
public string CustomerName
{
get
{
if(_customerName == null)
{
_customerName = _Customers.Where(c => c.IsReady).FirstOrDefault().Name;
}
return _customerName;
}
set
{
_customerName = value;
NotifyPropertyChanged("CustomerName");
}
}

WPF binding doesn't work

I have simplified example:
XAML:
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Name="cb" />
<Button Name="button1" Click="button1_Click" />
Code behind:
public partial class MainWindow : Window
{
private ObservableCollection<MyObject> collection = new ObservableCollection<MyObject>();
public MainWindow()
{
InitializeComponent();
collection.Add(new MyObject(true));
//grid.DataContext = collection[0];
}
private void button1_Click(object sender, RoutedEventArgs e)
{
collection[0].IsSelected = false;
}
}
public class MyObject
{
public bool IsSelected { get; set; }
public MyObject(bool isSelected)
{
this.IsSelected = isSelected;
}
}
The cb.IsChecked doesn't change by button clicking though the collection[0].IsSelected is changed.
Even if I uncomment grid.DataContext = collection[0]; - nothing changed.
In real example I have the same checkbox in the item template of a listbox. So the behaviour is the same - the selection of checkboxes don't change.
You need to implement INotifyPropertyChanged on your MyObject type
Please try the following codes:
public class MyObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
public MyObject(bool isSelected)
{
this.IsSelected = isSelected;
}
}

How to implement dependency object/property on custom class?

Here is my Person Class:
public class Person
{
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
}//close class
Here is my XAML:
<StackPanel>
<TextBox x:Name="txtLastName"
Height="50" Width="300"
DataContext="{Binding ElementName=_this, Path=PersonObject}"
Text="{Binding Path=LastName}" />
<Button Height="50" Width="100" x:Name="btnChangeValue" Content="Change Value" Click="btnChangeValue_Click"/>
</StackPanel>
Here is my XAML.CS
public partial class ClassDependency : Window
{
public Person objPerson = new Person();
public ClassDependency()
{
objPerson.LastName = "testing...";
InitializeComponent();
}
public Person PersonObject
{
get { return objPerson; }
set { objPerson = value; }
}
private void btnChangeValue_Click(object sender, RoutedEventArgs e)
{
objPerson.LastName = "New value after click....";
}
}//close class
My question is: After clicking "btnChangeValue" it does changing Last Name in my code behind but it is not reflection my textbox "txtLastName". How can I fix this??? Should I implement Dependency Property in my xaml.cs file?? I tried that too but no use.
public static readonly DependencyProperty PersonObjectProperty = DependencyProperty.Register("PersonObject", typeof(object), typeof(ClassDependency));
public Person PersonObject
{
get { return (Person)GetValue(PersonObjectProperty); }
set { SetValue(PersonObjectProperty, value); }
}
What should I do?? Please advice..
Try this:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
}
This way, the framework gets notified when the property changes. See INotifyPropertyChanged Interface.
The problem with your code is that you are not raising the PropertyChanged event, so the UI is not aware of the value change, on the setter of your dependency properties raise the PropertyChanged event as shown below:
public class Person : INotifyPropertyChanged
{
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
}

Bind IDictionary<string, object> on datagrid control in WPF

i would like bind idictionary on datagrid view.
Here is property which I bind on datagrid control.
public IDictionary<string, Bill> CellPhoneBills
{
get { return _cellPhoneBills; }
set
{
_cellPhoneBills = value;
NotifyOfPropertyChange(()=>CellPhoneBills);
}
}
Here is class Bill.
public class Bill : INotifyPropertyChanged
{
#region Private fields
private string _cellPhoneNo;
private BillData _vps;
#endregion
#region Properties
public string CellPhoneNo
{
get { return _cellPhoneNo; }
set
{
_cellPhoneNo = value;
NotifyPropertyChanged("CellPhoneNo");
}
}
public BillData Vps
{
get { return _vps; }
set
{
_vps = value;
NotifyPropertyChanged("Vps");
}
}
#endregion
#region Constructor
public Bill()
{
Vps = new BillData();
}
#endregion
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
BillData class:
public class BillData : INotifyPropertyChanged
{
#region Private fields
private int _time;
private double _price;
private List<Call> _calls;
#endregion
#region Properties
public int Time
{
get { return _time; }
set
{
_time = value;
NotifyPropertyChanged("Time");
}
}
public Double Price
{
get { return _price; }
set
{
_price = value;
NotifyPropertyChanged("Price");
}
}
public List<Call> Calls
{
get { return _calls; }
set
{
_calls = value;
NotifyPropertyChanged("Calls");
}
}
#endregion
#region Constructors
public BillData()
{
Calls = new List<Call>();
}
#endregion
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
Here is xaml:
<Controls:DataGrid
ItemsSource="{Binding Path= Bill, Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}">
<Controls:DataGrid.Columns>
<Controls:DataGridTextColumn IsReadOnly="True"
Binding="{Binding Path=Vps.Price}"
Header="Vps"/>
</Controls:DataGrid.Columns>
</Controls:DataGrid>
But datagrid is Blank.
I try convert dictionary value to array and bind this on datagrid.
public IList<Bill> Bill
{
get { return CellPhoneBills.Values.ToList();}
}
It didnt help.
What is root of problem?

Resources