set property from running thread to ViewModel in Caliburn Micro - c#

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

Related

Having a class with different method actions - Virtual or Delegate?

I have an Item class in C# and I want to have two methods, OnRightClick and OnLeftClick, but I want every item to do different things upon using them (I am on Unity and this class is not a monoBehavior).
I heard about virtual methods, but from what I realized they can be overridden only from other classes that inherit them. However, I want to do without making a separate class for every item I make. How can I make those 2 methods vary?
I thought of using delegate, but it doesn't work the way I expected it to either. Is it even possible?
EDIT
(Ik the following line are not a thing but this is the best way I can somehow explain what I want to do)
Imagine having the following simple class Item
public class Item
{
private string name;
private float weight;
private int price;
private bool dropable;
public Item(string name, float weight, int price, bool dropable)
{
this.name = name;
this.weight = weight;
this.price = price;
this.dropable = dropable;
}
public Item(string name, float weight, int price)
{
this.name = name;
this.weight = weight;
this.price = price;
this.dropable = true;
}
public string GetName()
{
return this.name;
}
public float GetWeight()
{
return this.weight;
}
public int GetPrice()
{
return this.price;
}
public bool GetDropable()
{
return this.dropable;
}
public void SetDropable(bool dropable)
{
this.dropable = dropable;
}
}
I want to be able to make an OnRightClick and OnLeftClick that would vary from every item I create if I could do something like(As I said I know it's not valid but this is the best way I can explain it, also didn't mention it in the class above, this is the original class I currently have)
Item item1 = new Item(*all the stuff here*);
Item item2 = new Item(*other stuff*);
item1.OnRightClick = delegate {*what I want on right click*};
item1.OnRightClick(); //executes the function for item1
item2.OnRightClick = delegate {*other stuff on right click*};
item2.OnRightClick(); //executes a different function for item2
Again, this is not a valid code but I just used this to try and explain what I want to try and do, and to ask if there is any solutions to this that exist. In the worst case, if there aren't, I could just use virtual but I'd like that to be my last case of no choice.
You can have a parent Item class then different children item classes that inherit from it the syntax it
public class MyItem : MonoBehaviour {
public enum ItemType {HEALING, OFFENSIVE, CONSUMABLE, EQUIPMENT}
public ItemType itemType;
public float potency;
public MyItem(ItemType _it, float _potency) {
itemType = _it;
potency = _potency;
}
public void OnRightClick() {
switch (itemType) {
case ItemType.HEALING:
HealCharacter(character, potency);
break;
case ItemType.OFFENSIVE:
break;
// more cases
}
}
public void OnLeftClick() {
//fill in like onrightclick
}
}
You can use Action and Func to do what you are asking.
https://learn.microsoft.com/en-us/dotnet/api/system.action-1?view=netcore-3.1
https://learn.microsoft.com/en-us/dotnet/api/system.func-2?view=netcore-3.1
public enum ItemType
{
Sword,
Shield,
Potion
}
public class Item
{
private readonly Action leftClickHandler;
private readonly Action<int> leftClickHandlerWithParam;
private readonly Action rightClickHandler;
private readonly Action<int> rightClickHandlerWithParam;
public Item(ItemType itemType)
{
switch (itemType)
{
case ItemType.Potion:
this.rightClickHandler = this.HandleClickWithFlash;
this.leftClickHandler = this.HandleClickWithSound;
this.leftClickHandlerWithParam = this.HandleClickWithFlash;
this.rightClickHandlerWithParam = this.HandleClickWithSound;
break;
}
}
public void HandleLeftClick()
{
this.leftClickHandler();
}
public void HandleRightClick()
{
this.rightClickHandler();
}
private void HandleClickWithFlash()
{
// Logic here.
}
private void HandleClickWithFlash(int parameter)
{
// Logic here.
}
private void HandleClickWithSound()
{
// Logic here.
}
private void HandleClickWithSound(int parameter)
{
// Logic here.
}
}
Here it is with the items exposed, if say you wanted a item factory concept.
public class ItemSettableHandlers
{
public ItemSettableHandlers()
{
}
public Action LeftClickHandler { get; set; }
public Action RightClickHandler { get; set; }
public void HandleLeftClick()
{
this.LeftClickHandler?.Invoke();
}
public void HandleRightClick()
{
this.RightClickHandler?.Invoke();
}
}
public class ItemCreator
{
public void CreateItems()
{
var itemSword = new ItemSettableHandlers();
itemSword.LeftClickHandler = () =>
{
// Do sword left click here.
};
itemSword.RightClickHandler = () =>
{
// Do sword right click here.
};
var itemShield = new ItemSettableHandlers();
itemShield.LeftClickHandler = () =>
{
// Do shield left click here.
};
itemShield.RightClickHandler = () =>
{
// Do shield right click here.
};
}
}
You could use EventHandler:
in you class, you define:
public event EventHandler RightClick;
public void OnRightClick()
{
EventHandler handler = RightClick;
if (null != handler) handler(this, EventArgs.Empty);
}
You use like that:
// Enable Event
RightClick += new EventHandler(OnRightClick);
OnRightClick();
public void OnRightClick(object s, EventArgs e)
{
}

How to update textblock in real-time?

I'm working on project with MVVM Light framework.
I have MainViewModel which helps me to navigate between viewmodels. I have GoBack and GoTo methods. Their are changing CurrentViewModel.
private RelayCommand<string> _goTo;
public RelayCommand<string> GoTo
{
get
{
return _goTo
?? (_goTo = new RelayCommand<string>(view
=>
{
SwitchView(view);
}));
}
}
private void SwitchView(string name)
{
switch (name)
{
case "login":
User = null;
CurrentViewModel = new LoginViewModel();
break;
case "menu":
CurrentViewModel = new MenuViewModel();
break;
case "order":
CurrentViewModel = new OrderViewModel();
break;
}
In MainWindow there is content control and data templates.
[...]
<DataTemplate DataType="{x:Type vm:LoginViewModel}">
<view:Login/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MenuViewModel}">
<view:Menu/>
</DataTemplate>
[...]
<ContentControl VerticalAlignment="Top" HorizontalAlignment="Stretch"
Content="{Binding CurrentViewModel}" IsTabStop="false"/>
In my OrderView (it is UserControl) I have textblock which should shows TotalPrice of order.
<TextBlock Text="{Binding AddOrderView.TotalPrice}" Padding="0 2 0 0" FontSize="20" FontWeight="Bold" HorizontalAlignment="Right"/>
OrderViewModel has a property TotalPrice and it works well. When I am debugging I see that it has been changed, but in my View nothing happend.
private decimal _totalPrice;
public decimal TotalPrice
{
get
{
_totalPrice = 0;
foreach (var item in Products)
{
item.total_price = item.amount * item.price;
_totalPrice += item.price * item.amount;
}
return _totalPrice;
}
set
{
if (_totalPrice == value)
return;
_totalPrice = value;
RaisePropertyChanged("TotalPrice");
}
}
OrderViewModel iherits from BaseViewModel and it implements INotifyPropertyChanged.
Why is my textblock not updating/refreshing? How to do this?
When I change view with back button and go again to OrderView I see changes!
I spend few days to looking for solution and nothing helps me.
https://i.stack.imgur.com/K8lip.gif
So it's look like when View is seting there is no way to change it without reload its. I don't know how it works exactly.
You shouldn't do calculations or any time consuming operations in the getter or setter of a property. This can drastically degrade performance. If the calculations or operation is time consuming, then you should execute it in a background thread and raise the PropertyChangedevent once the Taskhas completed. This way the invocation of the getter or setter of a property won't freeze the UI.
Explanation of the behavior you observed:
The side effect of changing the property value in its own a getter instead of the setter is that the new value won't propagate to the binding targets. The getter is only invoked by a binding when the PropertyChanged event occurred. So doing the calculations in the getter doesn't trigger the binding to refresh. Now when reloading the page, all bindings will initialize the binding target and therefore invoke the property getter.
You have to set the TotalPrice property (and not the backing field) in order to trigger a refresh of the binding target. But as you already experienced yourself, raising the PropertyChanged event of a property in the
same getter will lead to an infinite loop and therefore to a StackOverflowException.
Also the calculation will be always executed whenever the property's getter is accessed - even when the TotalPrice hasn't changed.
The value of TotalPrice depends on Products property. To minimize the occurrence of the calculation of TotalPrice, only calculate when Products has changed:
OrderViewModel.cs
public class OrderViewModel : ViewModelBase
{
private decimal _totalPrice;
public decimal TotalPrice
{
get => this._totalPrice;
set
{
if (this._totalPrice == value)
return;
this._totalPrice = value;
RaisePropertyChanged();
}
}
private ObservableCollection<Product> _products;
public ObservableCollection<Product> Products
{
get => this._products;
set
{
if (this.Products == value)
return;
if (this.Products != null)
{
this.Products.CollectionChanged -= OnCollectionChanged;
UnsubscribeFromItemsPropertyChanged(this.Products);
}
this._products = value;
this.Products.CollectionChanged += OnCollectionChanged;
if (this.Products.Any())
{
SubscribeToItemsPropertyChanged(this.Products);
}
RaisePropertyChanged();
}
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (!e.Action.Equals(NotifyCollectionChangedAction.Move))
{
UnsubscribeFromItemsPropertyChanged(e.OldItems);
SubscribeToItemsPropertyChanged(e.NewItems);
}
CalculateTotalPrice();
}
private void ProductChanged(object sender, PropertyChangedEventArgs e) => CalculateTotalPrice();
private void SubscribeToItemsPropertyChanged(IList newItems) => newItems?.OfType<INotifyPropertyChanged>().ToList().ForEach((item => item.PropertyChanged += ProductChanged));
private void UnsubscribeFromItemsPropertyChanged(IEnumerable oldItems) => oldItems?.OfType<INotifyPropertyChanged>().ToList().ForEach((item => item.PropertyChanged -= ProductChanged));
private void CalculateTotalPrice() => this.TotalPrice = this.Products.Sum(item => item.total_price);
private void GetProducts()
{
using (var context = new mainEntities())
{
var result = context.product.Include(c => c.brand);
this.Products = new ObservableCollection<Product>(
result.Select(item => new Product(item.name, item.mass, item.ean, item.brand.name, item.price)));
}
}
public void ResetOrder()
{
this.Products
.ToList()
.ForEach(product => product.Reset());
this.TotalPrice = 0;
}
public OrderViewModel()
{
SetView("Dodaj zamówienie");
GetProducts();
}
}
Also make sure that Product (the items in the Products collection) implements INotifyPropertyChanged too. This will ensure that Products.CollectionChanged event is raised when Product properties have changed.
To fix the page switching behavior you have to modify the MainViewModel class:
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
// The page viewmodels
private Dictionary<string, ViewModelBase> PageViewModels { get; set; }
public Stack<string> ViewsQueue;
public MainViewModel()
{
User = new User(1, "login", "name", "surname", 1, 1, 1);
this.PageViewModels = new Dictionary<string, ViewModelBase>()
{
{"login", new LoginViewModel()},
{"menu", new MenuViewModel()},
{"order", new OrderViewModel()},
{"clients", new ClientsViewModel(User)}
};
this.CurrentViewModel = this.PageViewModels["login"];
this.ViewsQueue = new Stack<string>();
this.ViewsQueue.Push("login");
Messenger.Default.Register<NavigateTo>(
this,
(message) =>
{
try
{
ViewsQueue.Push(message.Name);
if (message.user != null) User = message.user;
SwitchView(message.Name);
}
catch (System.InvalidOperationException e)
{
}
});
Messenger.Default.Register<GoBack>(
this,
(message) =>
{
try
{
ViewsQueue.Pop();
SwitchView(ViewsQueue.Peek());
}
catch (System.InvalidOperationException e)
{
}
});
}
public RelayCommand<string> GoTo => new RelayCommand<string>(
viewName =>
{
ViewsQueue.Push(viewName);
SwitchView(viewName);
});
protected void SwitchView(string name)
{
if (this.PageViewModels.TryGetValue(name, out ViewModelBase nextPageViewModel))
{
if (nextPageViewModel is OrderViewModel orderViewModel)
orderViewModel.ResetOrder();
this.CurrentViewModel = nextPageViewModel;
}
}
}
Your modifified Product.cs
public class Product : ViewModelBase
{
public long id { get; set; }
public string name { get; set; }
public decimal mass { get; set; }
public long ean { get; set; }
public long brand_id { get; set; }
public string img_source { get; set; }
public string brand_name { get; set; }
private decimal _price;
public decimal price
{
get => this._price;
set
{
if (this._price == value)
return;
this._price = value;
OnPriceChanged();
RaisePropertyChanged();
}
}
private long _amount;
public long amount
{
get => this._amount;
set
{
if (this._amount == value)
return;
this._amount = value;
OnAmountChanged();
RaisePropertyChanged();
}
}
private decimal _total_price;
public decimal total_price
{
get => this._total_price;
set
{
if (this._total_price == value)
return;
this._total_price = value;
RaisePropertyChanged();
}
}
public Product(long id, string name, decimal mass, long ean, long brandId, decimal price, string imgSource)
{
this.id = id;
this.name = name;
this.mass = mass;
this.ean = ean;
this.brand_id = brandId;
this.price = price;
this.img_source = imgSource;
}
public Product(string name, decimal mass, long ean, string brandName, decimal price)
{
this.id = this.id;
this.name = name;
this.mass = mass;
this.ean = ean;
this.brand_name = brandName;
this.price = price;
}
public void Reset()
{
// Resetting the `amount` will trigger recalculation of `total_price`
this.amount = 0;
}
protected virtual void OnAmountChanged()
{
CalculateTotalPrice();
}
private void OnPriceChanged()
{
CalculateTotalPrice();
}
private void CalculateTotalPrice()
{
this.total_price = this.price * this.amount;
}
}
The issue was that you have always created a new view model when switching to a page. Of course all previous page information gets lost. You have to reuse the same view model instance. To do this, just store them in a dedicated private property that you initialize once in the constructor.
It's not updating because you're only calling RaisePropertyChanged("TotalPrice"); in the setter. Whereas in your getter is the calculation. So anytime you change the Products property or the content of the Products collection, you also need to call RaisePropertyChanged("TotalPrice"); to notify the View that TotalPrice has been updated.
So if you change any of the item.amount or item.price or if you add or remove items from the Products list then you need to also call. RaisePropertyChanged("TotalPrice");
Ex:
Products.Add(item);
RaisePropertyChanged("TotalPrice"); //This will tell you're View to check for the new value from TotalPrice

C# WPF DataGridTextColumn custom property

I'm trying to bind data to some sort of custom dependency property or attached property in WPF. I've used the following question
and answer- How to create Custom Property for WPF DataGridTextColumn
as a frame of reference but I need to bind to the 'tag' in WPF. We can call it whatever, but I call it a tag for simplicity as I'm using it on some checkboxes and textboxes in other parts of the code. My Class is as follows:
public class TagTextColumns : DependencyObject
{
public static readonly DependencyProperty TagProperty = DependencyProperty.RegisterAttached(
"Tag",
typeof(object),
typeof(DataGridColumn),
new FrameworkPropertyMetadata(null));
public static object GetTag(DependencyObject dependencyObject)
{
return dependencyObject.GetValue(TagProperty);
}
public static void SetTag(DependencyObject dependencyObject, object value)
{
dependencyObject.SetValue(TagProperty, value);
}
}
I'd like to setup my DataGridTextColumn in WPF to something similar to the following:
<DataGridTextColumn Binding="{Binding Customer}" Header="Customer" local:TagTextColumns.Tag="{Binding tkey}"/>
where tkey is a reference in the db. I do all this to calculate subtotals in the last column per row. Right now, I use a DataGridTemplateColumn with a TextBox inside so I can Tag it with tkey
I've also read up on TechNet about these things but there seems to be little pointing to what exactly I want to do here. I'm not even sure it's possible, but it would seem like it would be.
EDIT:
This is the code I'm using to attempt to call the tag on CellEditEndingfiring
private void resultsDg_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
MessageBox.Show(TagTextColumns.GetTag(sender as DataGridTextColumn).ToString());
}
More details about the problem at hand:
I am trying on four fields from the database to dynamically calculate the subtotal for the last column as the user makes changes. I was trying to use Linq to SQL for this, but using System.Data.SqlClient and System.Data was much faster. Maybe someone can show me a better way. The calculation code is below:
private void CalculateAscessorials(string tkey)
{
decimal SplitTerm = Properties.Settings.Default.SplitTerminal;
decimal SplitDrop = Properties.Settings.Default.SplitDrop;
decimal SiteSplit = Properties.Settings.Default.OnSiteSplit;
decimal Trainer = Properties.Settings.Default.TrainerLoad;
decimal WaitTime = Properties.Settings.Default.TerminalWait;
decimal PumpOut = Properties.Settings.Default.PumpOut;
DataRow[] row = resultDetail.Select("tkey = " + tkey);
decimal payRate;
decimal tempLineItem;
Decimal.TryParse(row[0]["PayForLine"].ToString(), out tempLineItem);
Decimal.TryParse(row[0]["PayRate"].ToString(), out payRate);
if (payRate == 0 && tempLineItem > 0) //this change affts if the rate is 0, that is no rate defined, the rate entered becomes the pay rate for that line only for calculations
row[0]["PayRate"] = tempLineItem;
if (!Convert.ToBoolean(row[0]["SplitDrop"]))
{
Decimal.TryParse(row[0]["PayRate"].ToString(), out payRate);
}
else if (Convert.ToBoolean(row[0]["SplitDrop"]))
{
payRate = SplitDrop;
}
//decimal linePay;
// Decimal.TryParse(row[0]["PayForLine"].ToString(), out linePay);
int terms;
Int32.TryParse(row[0]["SplitLoad"].ToString(), out terms);
decimal waits;
Decimal.TryParse(row[0]["WaitTime"].ToString(), out waits);
int pumps;
Int32.TryParse(row[0]["PumpOut"].ToString(), out pumps);
int sites;
Int32.TryParse(row[0]["SiteSplit"].ToString(), out sites);
int trainings;
Int32.TryParse(row[0]["Trainer"].ToString(), out trainings);
row[0]["PayForLine"] =
(SplitTerm * terms)
+ (waits * WaitTime)
+ (pumps * PumpOut)
+ (sites * SiteSplit)
+ (trainings * Trainer)
+ payRate;
}
You can use the MVVM pattern to calculate the subtotals in the ViewModel. Here's a example that should lead you to the right direction. In the constructor of the MainView I create 2 courses and apply students. A course has a property TotalWeight which calculates the total weight of all students. If the weight of a student changes, or a student is added/removed to the course we have to update the value on the UI. This due to INotifyPropertyChanged and ObservableCollection.
If you use LinqToSQL or even better EntityFramework you can get from store. You could than inject it into the ViewModel constructor as the acutal model. Maybe you even want to introduce a MainViewModel for this purpose. I skipped this for simplicity. But you may want to have a look on dependency injection.
XAML:
<DataGrid ItemsSource="{Binding}">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Students}"></DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
code behind:
public class CourseViewModel : ViewModelBase
{
#region Fields
private string _name;
private ObservableCollection<StudentViewModel> _students;
#endregion Fields
#region Constructors
public CourseViewModel()
{
_students = new ObservableCollection<StudentViewModel>();
_students.CollectionChanged += _students_CollectionChanged;
}
#endregion Constructors
#region Properties
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged();
}
}
public ObservableCollection<StudentViewModel> Students
{
get
{
return _students;
}
}
public double TotalWeight
{
get
{
return Students.Sum(x => x.Weight);
}
}
#endregion Properties
#region Methods
private void _students_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// add/remove property changed handlers to students
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (StudentViewModel student in e.NewItems)
{
student.PropertyChanged += Student_PropertyChanged;
}
}
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
{
foreach (StudentViewModel student in e.OldItems)
{
student.PropertyChanged -= Student_PropertyChanged;
}
}
//students were added or removed to the course -> inform "listeners" that TotalWeight has changed
OnPropertyChanged(nameof(TotalWeight));
}
private void Student_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//the weight of a student has changed -> inform "listeners" that TotalWeight has changed
if (e.PropertyName == nameof(StudentViewModel.Weight))
{
OnPropertyChanged(nameof(TotalWeight));
}
}
#endregion Methods
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
#region Constructors
public MainWindow()
{
InitializeComponent();
var course1 = new CourseViewModel() { Name = "Course1" };
course1.Students.Add(new StudentViewModel() { Weight = 100, Name = "Mark" });
course1.Students.Add(new StudentViewModel() { Weight = 120, Name = "Olaf" });
course1.Students.Add(new StudentViewModel() { Weight = 111, Name = "Hans" });
var course2 = new CourseViewModel() { Name = "Course2" };
course2.Students.Add(new StudentViewModel() { Weight = 100, Name = "Mark" });
course2.Students.Add(new StudentViewModel() { Weight = 90, Name = "Renate" });
course2.Students.Add(new StudentViewModel() { Weight = 78, Name = "Judy" });
DataContext = new List<CourseViewModel>()
{
course1,
course2
};
}
#endregion Constructors
}
public class StudentViewModel : ViewModelBase
{
#region Fields
private string _name;
private double _weight;
#endregion Fields
#region Properties
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged();
}
}
public double Weight
{
get
{
return _weight;
}
set
{
_weight = value;
OnPropertyChanged();
}
}
#endregion Properties
}

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}/>

Binding label content to a value in nested class not in Datacontext

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}" />

Categories

Resources