Binding label content to a value in nested class not in Datacontext - c#

My view (InformationView) Binded to InformationViewModel and I use a nested class to maintain current Bank
My nested class :
public class MainController : NotificationObject
{
public MainController()
{
Initialize();
}
private void Initialize()
{
// TODO implement
}
public static MainController Instance
{
get { return Nested.instance; }
}
private BankModel _currentBank;
public BankModel CurrentBank
{
get { return _currentBank; }
set
{
if (_currentBank== value)
{
return;
}
_currentBank= value;
RaisePropertyChanged(() => CurrentBank);
}
}
private class Nested
{
static Nested()
{
}
internal static readonly MainController instance = new MainController();
}
}
My BankModel :
private string _name ="test";
public string Name
{
get
{
return _name;
}
set
{
if (_name == value)
{
return;
}
_name= value;
RaisePropertyChanged(()=>Name);
}
}
My XAML
xmlns:Controller="clr-namespace:MyProject.Controller"
/****/
<Label Content="{Binding Controller:MainController.CurrentBank.Name}"/>
first I can't see the "test" in my label and if I execute I change this value and always my label is empty, how I do this with the correct approach

You need to use a combination of "Path" and "Source" in your binding declaration. You also need to alert the binding engine to the fact that you're accessing static members.
<Label Content="{Binding Source={x:Static Controller:MainController.Instance}, Path=CurrentBank.Name}" />

Related

Implementing RaiseCanExecuteChanged method inside setter of a class object

I am still not sure if my approach is correct, but in an attempt to implement the MVVM pattern, I have created a model class 'Test' in the following way:
public class Test : BindableBase
{
private int testNumber;
public int TestNumber
{
get { return testNumber; }
set { SetProperty(ref testNumber, value) }
}
...
}
Then I created an instance of this class in my ViewModel:
class ViewModel : BindableBase
{
private Test testVM;
public Test TestVM
{
get { return testVM; }
set { SetProperty(ref testVM, value); }
}
...
And in the XAML code of the View I bind all the properties of the Test class through the TestVM property. Although this works fine, I ran into a problem when trying to implement a DelegateCommad.
public DelegateCommand StartTestCommand { get; private set; }
So far, when implementing DelegateCommands, if I want to trigger the CanExecute method when a property has changed, I include DelegateCommand.RaiseCanExecuteChanged() inside the property's setter. Like so:
...
private bool duringTest;
public bool DuringTest
{
get { return duringTest; }
set
{
SetProperty(ref duringTest, value);
StartTestCommand.RaiseCanExecuteChanged();
}
}
...
This works fine for properties declared in the ViewModel, but when using the same approach for the Test properties, this no longer works.
...
private Test testVM;
public Test TestVM
{
get { return testVM; }
set
{
SetProperty(ref testVM, value);
StartTestCommand.RaiseCanExecuteChanged();
}
}
}
I would expect that every time a property from TestVM was changed, the setter would be called, but instead the model is updated directly.
What am I doing wrong? What is the correct approach when using a Model object in the ViewModel?
Changing a property value of an object doesn't change the object's reference.
Declaring this
public Test TestVM
{
get { return testVM; }
set
{
SetProperty(ref testVM, value);
StartTestCommand.RaiseCanExecuteChanged();
}
}
you are basically telling the compiler: when the reference to the TestVM object is changed (even to the same value), update the StartTestCommand's state.
But obviously you don't change the reference to that object once you assigned it.
If you want to update the commands in your parent view-model (ViewModel) when some child view-model's (Test) properties change, you can use the PropertyChanged event:
public Test TestVM
{
get { return testVM; }
set
{
Test oldValue = testVM;
if (SetProperty(ref testVM, value))
{
if (oldValue != null)
{
oldValue.PropertyChanged -= TestPropertyChanged;
}
if (testVM!= null)
{
testVM.PropertyChanged += TestPropertyChanged;
}
}
}
}
void TestPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// filter if necessary
if (e.PropertyName == "...")
{
StartTestCommand.RaiseCanExecuteChanged();
}
}

Changing protection level of newly created object?

Alright, so I have luck of running into a lot of basic problems. I can't figure a way around this particular issue.
This piece of code needs to access "_Player.Name" property of object created in "MainWindow" class.
Edit: Putting up the whole code this time. Here's the Code_Behind where the string is.
public class Code_Behind
{
private static string _Name = "Default";
public class Player
{
public void setName(string name) //Ignore this part, was trying to find a work around here
{
_Name = name;
}
public string Name
{
get { return _Name; }
set
{
_Name = value;
}
}
}
//contentControl is used to store Content properties
//UI elements are bound to Content properties to efficiently change their Content
public class contentControl : INotifyPropertyChanged
{
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public void setEvent(string Event)
{
textBoxContent = Event;
}
public void addEvent(string Event)
{
textBoxContent +="\n" + Event;
}
public class Events
{
public string EV001 = String.Format("\"Greetings {0}. What can I do for you today?\"", window.PlayerName);
}
}
And here is the MainWindow one:
public partial class MainWindow : Window
{
Code_Behind.contentControl cC = new Code_Behind.contentControl();
Code_Behind.contentControl.Events Events = new Code_Behind.contentControl.Events();
Code_Behind.Player _Player = new Code_Behind.Player();
public string GetPlayerName()
{
return _Player.Name;
}
public static string _name = "null";
public MainWindow()
{
this.DataContext = cC;
InitializeComponent();
}
public string GetPlayerName()
{
return _Player.Name
}
Create a method in your MainWindow class. After that you call this method.
public string EV001 = String.Format("\"Greetings {0}. What can I do for you today?\"",
window.GetPlayerName());
You can do it with property too if you want.
public string PlayerName
{
get { return _Player.Name; };
}
The bigger problem you have here is not about accessibility, but not understanding the difference between a class and an object.
MainWindow is a class. It does not represent any specific window. Think of a class like a recipe to create objects. If you had a chocolate chip cookie recipe, you don't eat the recipe, you eat a specific cookie or cookies baked following that recipe.
Your other class first needs to know which specific window you are trying to get the player name from. It needs a reference to a particular MainWindow object.
It looks like you're trying write something like a viewmodel: You've got a player, he has a name, and there's a collection of strings that you think of as "events". I don't understand what the "events" are meant to be, but I implemented my best guess at what I think you seem to be trying to do.
As for this:
public class Events
{
public string EV001 = String.Format("\"Greetings {0}. What can I do for you today?\"", window.PlayerName);
}
I guess you created an instance of MainWindow somewhere, and called it window, but it's defined someplace where it's "out of scope" for that line of code. By analogy, you can't see anything that's behind the next hill, only stuff that's in the valley you're standing in. That's roughly (very roughly, sorry) kind of what scope is about.
But let's move on to my guess at what you're trying to do. This builds, runs, and works. Any questions at all, fire away.
ViewModels.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Player
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class MainViewModel : ViewModelBase
{
#region Player Property
private PlayerViewModel _player = default(PlayerViewModel);
public PlayerViewModel Player
{
get { return _player; }
set
{
if (value != _player)
{
_player = value;
OnPropertyChanged(nameof(Player));
// Change the player for all the existing events.
foreach (var e in Events)
{
e.Player = Player;
}
}
}
}
#endregion Player Property
private ObservableCollection<Event> _events = new ObservableCollection<Event>();
public ObservableCollection<Event> Events
{
get { return _events; }
private set
{
if (value != _events)
{
_events = value;
OnPropertyChanged(nameof(Events));
}
}
}
#region Event Methods
// This is a BIG guess as to what you're trying to do.
public void AddGreeting()
{
// Player is "in scope" because Player is a property of this class.
if (Player == null)
{
throw new Exception("Player is null. You can't greet a player who's not there.");
}
Events.Add(new Event("\"Greetings {0}. What can I do for you today?\"", Player));
}
#endregion Event Methods
}
public class Employee : ViewModelBase
{
#region DisplayLtdOccupationId Property
private bool _displayLtdOccupationId = default(bool);
public bool DisplayLtdOccupationId
{
get { return _displayLtdOccupationId; }
set
{
if (value != _displayLtdOccupationId)
{
_displayLtdOccupationId = value;
OnPropertyChanged(nameof(DisplayLtdOccupationId));
}
}
}
#endregion DisplayLtdOccupationId Property
}
public class Event : ViewModelBase
{
public Event(String format, PlayerViewModel player)
{
_format = format;
Player = player;
}
private String _format = "";
public String Message
{
get { return String.Format(_format, Player.Name); }
}
#region Player Property
private PlayerViewModel _player = default(PlayerViewModel);
public PlayerViewModel Player
{
get { return _player; }
set
{
if (value != _player)
{
_player = value;
OnPropertyChanged(nameof(Player));
// When player changes, his name changes, so that
// means the value of Message will change.
OnPropertyChanged(nameof(Message));
if (_player != null)
{
_player.PropertyChanged += _player_PropertyChanged;
}
}
}
}
private void _player_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(PlayerViewModel.Name):
OnPropertyChanged(nameof(Message));
break;
}
}
#endregion Player Property
}
public class PlayerViewModel : ViewModelBase
{
private String _name = default(String);
public String Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
}
}
MainWindow.xaml.cs
using System.Windows;
namespace Player
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel = new MainViewModel();
ViewModel.Player = new PlayerViewModel() { Name = "Ivan the Terrible" };
}
// Just here as a convenience, and to make sure we don't give the DataContext
// the wrong kind of viewmodel.
public MainViewModel ViewModel
{
set { DataContext = value; }
get { return DataContext as MainViewModel; }
}
private void Greeting_Click(object sender, RoutedEventArgs e)
{
ViewModel.AddGreeting();
}
}
}
MainWindow.xaml
<Window
x:Class="Player.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:Player"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical">
<WrapPanel>
<Button x:Name="Greeting" Content="Greeting" Click="Greeting_Click" />
<Label>Name: </Label>
<TextBox Text="{Binding Player.Name}" Width="120" />
</WrapPanel>
<ListBox
ItemsSource="{Binding Events}"
DisplayMemberPath="Message"
>
</ListBox>
</StackPanel>
</Grid>
</Window>
You can change the set of Name to be private, but still allow the outside world to read the property with the get.
public string Name { get; private set; } = "Default";
This should give you the functionallity desired without the need to create a new GetName() method.

INotifyPropertyChanged and property on BaseClass C#

I am working with WPF and MVVM model. I have a base viewmodel class called ViewModelBase. It has a property on it called Config that is a complex type. I need a derived class to be able to databind to the base class Config property in a View.
public class ViewModelBase : INotifyPropertyChanged
{
private Configuration _config;
public event PropertyChangedEventHandler PropertyChanged;
public Configuration Config
{
get { return _config; }
set
{
if(_config == null || !_config.Equals(value))
{
_config = value;
OnPropertyChanged(new PropertyChangedEventArgs("Config"));
}
}
}
public ViewModelBase()
{
}
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if(PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
}
Databinding seems to be working in a read capacity, but when a property of the Config is altered in the OptionsView, the changes are not reflected in the Config itself. Any suggestions?
Configuration implementation, per request.
public class Configuration : IEquatable<Configuration>, INotifyPropertyChanged
{
private string _primaryUrl;
private string _secondaryUrl;
private DateTime _scheduledStart;
private DateTime _scheduledEnd;
private string _buffer;
private bool _isScheduleEnabled;
private int _logDays;
private int _retryDuration;
private int _maxRetryAttempts;
private string _registrationKey;
private string _email;
public string PrimaryURL
{
get { return _primaryUrl; }
set
{
if(_primaryUrl != value)
{
_primaryUrl = value;
OnPropertyChanged(new PropertyChangedEventArgs("PrimaryURL"));
}
}
}
public string SecondaryURL
{
get { return _secondaryUrl; }
set
{
if(_secondaryUrl != value)
{
_secondaryUrl = value;
OnPropertyChanged(new PropertyChangedEventArgs("SecondaryURL"));
}
}
}
public DateTime ScheduledStart
{
get { return _scheduledStart; }
set
{
if(_scheduledStart != value)
{
_scheduledStart = value;
OnPropertyChanged(new PropertyChangedEventArgs("ScheduledStart"));
}
}
}
public DateTime ScheduledEnd
{
get { return _scheduledEnd; }
set
{
if(_scheduledEnd != value)
{
_scheduledEnd = value;
OnPropertyChanged(new PropertyChangedEventArgs("ScheduledEnd"));
}
}
}
public string Buffer
{
get { return _buffer; }
set
{
if(_buffer != value)
{
_buffer = value;
OnPropertyChanged(new PropertyChangedEventArgs("Buffer"));
}
}
}
public bool IsScheduleEnabled
{
get { return _isScheduleEnabled; }
set
{
if(_isScheduleEnabled != value)
{
_isScheduleEnabled = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsScheduleEnabled"));
}
}
}
public int LogDays
{
get { return _logDays; }
set
{
if(_logDays != value)
{
_logDays = value;
OnPropertyChanged(new PropertyChangedEventArgs("LogDays"));
}
}
}
public int RetryDuration
{
get { return _retryDuration; }
set
{
if(_retryDuration != value)
{
_retryDuration = value;
OnPropertyChanged(new PropertyChangedEventArgs("RetryDuration"));
}
}
}
public int MaxRetryAttempts
{
get { return _maxRetryAttempts; }
set
{
if(_maxRetryAttempts != value)
{
_maxRetryAttempts = value;
OnPropertyChanged(new PropertyChangedEventArgs("MaxRetryAttempts"));
}
}
}
public string RegistrationKey
{
get { return _registrationKey; }
set
{
if(_registrationKey != value)
{
_registrationKey = value;
OnPropertyChanged(new PropertyChangedEventArgs("RegistrationKey"));
}
}
}
public string Email
{
get { return _email; }
set
{
if(_email != value)
{
_email = value;
OnPropertyChanged(new PropertyChangedEventArgs("Email"));
}
}
}
public Configuration() { }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if(PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
}
Here is one of the culprit bindings:
<xctk:DateTimePicker Grid.Column="1" Value="{Binding Config.ScheduledStart}" Height="20" VerticalAlignment="Top"/>
The INotifyPropertyChanged implementation only applies to the class directly. So in your case, to the ViewModelBase class and its subtypes.
In this case, the PropertyChangedEvent is raised in the setter of the Config property, so whenever the Config property is set (and the setter is called), the event is raised.
This however does not mean that when mutating the Config object that the even is also raised. In general, this is not the case.
In order to raise the event when the Config object is changed, you would have to reassign the object to the view model (calling the setter again). This however does not work when data binding to the object.
A better solution is to make the Configuration implement INotifyPropertyChanged interface itself. So when a property within that object is changed, an event is raised as well. WPF will also recognize this for subobjects, so it will automatically work.
Databinding seems to be working in a read capacity..
Which is fine but if you want a change capacity, then the class Configuration will have to adhere to INotifyPropertyChanged and each property on the class needs to report PropertyChange notifications for any changes to be shown in bound xaml controls.
but when a property of the Config is altered in the OptionsView, the changes are not reflected
What you have now only notifies if the instance of Configuration has been replaced; not individual property changes within the current instance.

set property from running thread to ViewModel in Caliburn Micro

I'm beginner in Caliburn.Micro so if something is not clear or this implementation is wrong, please let me know. I have a DataGridViewModel with properties Name, Data
class DataGridViewModel : Screen
{
private char name;
public char Name
{
get { return name; }
set
{
name = value;
this.NotifyOfPropertyChange(() => Name);
}
}
private DataTable data;
public DataTable Data
{
get { return data; }
set
{
data = value;
this.NotifyOfPropertyChange(() => Data);
}
}
}
and a MainViewModel with properties Fitness and GenerationCout. In a MainView is a button which starts a new thread of my Genetic class.
public class MainViewModel : Conductor<Screen>.Collection.AllActive
{
private Genetic genetic;
private double fitness;
public double Fitness
{
get { return fitness; }
set
{
fitness = value;
NotifyOfPropertyChange(() => Fitness);
}
}
private int generationCout;
public int GenerationCout
{
get { return generationCout; }
set
{
generationCout = value;
NotifyOfPropertyChange(() => GenerationCout);
}
}
public void Start()
{
List<string> features = new List<string> { "(a*b)*c=a*(b*c)" };
genetic = new Genetic(features);
genetic.Start();
}
public MainViewModel()
{
generationCout = 0;
fitness = 0;
}
}
There are fields like best and generationCount. And there is a method SetActual() which is using in running thread of this class. In the method I'm setting actual fields value and I want to sent it to ViewModels eventually refresh it in Views.
public class Genetic : BaseThread
{
private int generationCount;
private Subject best;
private void SetActual()
{
DataTable data;
char name;
double fitness;
foreach (Operation operation in best.Operations)
{
DataTable data;
data = Converter.ArrayToDataTable(operation);
name = operation.Name;
fitness = best.Fitness;
}
generationCount++;
}
}
So I need show an actual value of those fields in my views during thread is running. Can anyone tell me how to do that with use the right approach?
You can easily notify Genetic's properties change from Genetic class itself by extend PropertyChangedBase and expose it directly from your ViewModel then refer to Genetic class property on MainViweModel from control
public class MainViewModel : Conductor<Screen>.Collection.AllActive
{
private Genetic genetic;
public Genetic CurrentGenetic
{
get { return genetic; }
set
{
genetic = value;
NotifyOfPropertyChange(nameof(CurrentGenetic));
}
}
}
public class Genetic : PropertyChangedBase
{
private int generationCount;
public int GenerationCout
{
get { return generationCount; }
set
{
generationCount = value;
NotifyOfPropertyChange(nameof(CurrentGenetic));
}
}
}
// example binding
<TextBlock Text="{Binding Path=CurrentGenetic.GenerationCout }" />
// or caliburn micro convention
<TextBlock x:Name="CurrentGenetic_GenerationCout" />
if you don't want to extend PropertyChangedBase, you can implement INotifyPropertyChangedEx(caliburn micro versioin of INotifyPropertyChanged) as https://github.com/Caliburn-Micro/Caliburn.Micro/blob/master/src/Caliburn.Micro/PropertyChangedBase.cs
more about Conventions see All about Conventions

2 class inheritance NotifyPropertyChanged fails update UI

I have currently the problem that my UI doesnt update as I like to do so, hope you can help me out.
I have a simulated "2 class inheritance" as recommended in this page
http://www.codeproject.com/Articles/10072/Simulated-Multiple-Inheritance-Pattern-for-C
My real life app looks like the following:
public class Item : INotifyPropertyChanged
{
private bool _isVisible;
public bool IsVisible
{
get
{
return _isVisible;
}
set
{
if (_isVisible == value)
return;
_isVisible = value;
OnPropertyChanged("IsVisible");
}
}
//NotifyPropertyChanged Implementation removed so the focus stays on problem...
}
public class ObjectItem : INotifyPropertyChanged
{
private bool _isExpanded;
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
if (_isExpanded== value)
return;
_isExpanded= value;
OnPropertyChanged("IsExpanded");
}
}
//NotifyPropertyChanged Implementation removed so the focus stays on problem...
}
public class CombinedItem : Item
{
private readonly ObjectItem _objectItem = new ObjectItem();
public bool IsExpanded
{
get { return _objectItem.IsExpanded; }
set { _objectItem.IsExpanded = value; }
}
public static implicit operator ObjectItem(CombinedItem combinedItem)
{
return combinedItem._objectItem;
}
}
I am now facing the problem that the Property IsExpanded doesnt get Notified to the UI correctly when I have a CominedItem as the DataContext, the IsVisible Property works as expected.
To overcome the problem I have changed the CominedItem to the following:
public class CombinedItem : Item
{
private readonly ObjectItem _objectItem = new ObjectItem();
public bool IsExpanded
{
get { return _objectItem.IsExpanded; }
set
{
if (_objectItem.IsExpanded == value)
return;
_objectItem.IsExpanded = value;
OnPropertyChanged("IsExpanded");
}
}
public static implicit operator ObjectItem(CombinedItem combinedItem)
{
return combinedItem._objectItem;
}
}
Is there a way to avoid writing the OnPropertyChanged("IsExpanded") again, with this approach.
(I know there are libaries/tools, where you dont need to write it at all and just have to declare a attribute, pls dont suggest those)
Actually you should subscribe to ObjectItem PropertyChanged and raise the matching event on CombinedItem.
If _objectItem.IsExpanded is modified without using CombinedItem.IsExpanded, your UI will not see the change.
Without some magic attribute/tool if you want to wrap a property, you will have to handle changes notification.
public class CombinedItem : Item
{
private readonly ObjectItem _objectItem = new ObjectItem();
public CombinedItem()
{
_objectItem.PropertyChanged += (s, e) =>
{
if (e.PropertyName == "IsExpanded")
OnPropertyChanged("IsExpanded");
}
}
public bool IsExpanded
{
get { return _objectItem.IsExpanded; }
set { _objectItem.IsExpanded = value; }
}
public static implicit operator ObjectItem(CombinedItem combinedItem)
{
return combinedItem._objectItem;
}
}
You could expose ObjectItem as a property and bind to that
public class CombinedItem : Item
{
private readonly ObjectItem _objectItem = new ObjectItem();
public bool IsExpanded
{
get { return _objectItem.IsExpanded; }
set { _objectItem.IsExpanded = value; }
}
public ObjectItem ObjectItem
{
get { return _objectItem; }
}
public static implicit operator ObjectItem(CombinedItem combinedItem)
{
return combinedItem._objectItem;
}
}
<Expander IsExpanded={Binding ObjectItem.IsExpanded}/>

Categories

Resources