How do I "Invalidate" after adding items to a collection property? - c#

I am creating a Custom control in where I am creating a property of the type "List"
Sections is a public class which has 4 properties.
The code in the control looks as follows:
public partial class genericGauge : Control
{
public genericGauge()
{
InitializeComponent();
}
// Stripped out code not needed for this issue question.
private List<Sections> indicators = new List<Sections>();
public List<Sections> Indicators
{
get
{
return indicators;
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Stripped out code not needed for this issue question.
}
}
The Sections Class is as follows:
public class Sections
{
private string header = "Section1";
public string Header
{
get {return header;}
set
{
header = value;
}
}
private float startvalue = 0.0f;
public float StartValue
{
get { return startvalue; }
set
{
startvalue = value;
}
}
private float sweepvalue = 0.0f;
public float SweepValue
{
get { return sweepvalue; }
set
{
sweepvalue = value;
}
}
private Color sectioncolor = new Color();
public Color SectionColor
{
get {return sectioncolor;}
set
{
sectioncolor = value;
}
}
}
Everything seems to work fine except that when I add items to the collection at designtime using the property browsers typeeditor the control is not repainted to reflect what is added to the collection.
When I click outside the control on my testform it is repainted.
Usually with simple properties I would use Invalidate, but this seems not to be possible here.
I also tried with other collection types than List<> where it is allowed to have a set accessor, but Invalidate still wont be called. I assume that it means that the SET is never called.
I know how to get this to work with expandable properties but I have no luck finding how to make this update with collections.
I hope someoone can help me out.
thanks in advance.

Instead of using the class List, use the class ObservableCollection, and use that to get notified when a new section is added or removed from the list.
private ObservableCollection<Sections> indicators = new ObservableCollection<Sections>();
public IList<Sections> Indicators
{
get
{
return indicators;
}
}
public genericGauge()
{
InitializeComponent();
this.indicators.CollectionChanged += this.IndicatorsCollectionChanged;
}
private void IndicatorsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// possibly inspect the NotifyCollectionChangedEventArgs to see if it's a change that should cause a redraw.
// or not.
this.Invalidate();
}

When using your example exactly as it was the Indicators property was not available for editing in the property window. So I made a few changes to it.
I added a new class:
// Added this class to deal with the Sections class
public class SectionObservable : ObservableCollection<Sections>
{
// Added a few methods here for creating a designtime collection if I need to.
}
Then I made the change as you suggested
public genericGauge()
{
InitializeComponent();
this.indicators.CollectionChanged += this.IndicatorsCollectionChanged; // your suggestion
}
And made the property like this instead:
private SectionObservable indicators = new SectionObservable(); // using the SectionObservable class instead
public SectionObservable Indicators // using the SectionObservable class instead
{
get
{
return indicators;
}
}
private void IndicatorsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) // your suggestion
{
this.Invalidate();
}
And now works as a charm.
Thank you very much. I appreciate to see that it IS possible to get help this fast. I like this forum alot.

Related

Cannot add children to a Grid in WPF

There are two ways that I tried to add children to a Grid. One is working but it is not prefered to me because I have ViewModel class. And I want to apply it in that class. So the another way is not working correctly, actually, it is not added as children to Grid. The last way is applied in ViewModel class. Here is two ways that I applided:
1- Working way.
public LaserLib fiber1 = new LaserLib();
private void MarkInit()
{
fiber1.Init(); // that is my specialized laser library.
myGrid.Children.Add(fiber1); // when I do this, I can see the fiber1 condition in grid.
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
MarkInit();
}
But, I have many of this and I want to appy to my viewModel.
What I tried:
2- Not worked. Way 1:
public class ViewModels
{
public ObservableCollection<MarkingManualStatuses> markingManualStatuses { get; set;}
= new ObservableCollection<MarkingManualStatuses(Enumerable.Range(0,4).Select(i => new MarkingManualStatuses()));
}
public class MarkingManualStatuses : INotifyPropertyChanged
{
public LaserLib Tmc { get; set; }
public void TmcInitialize()
{
Tmc = new LaserLib ();
Tmc.Init();
}
}
I run TmcInitialize() function at MainWindow_Onloaded
Everything is fine for now. My LaserLib gets initialize. it works. But, I cannot add it to grid.
Here is where I tried to add at ManualPage:
private void listViewMarkingInfos_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TmcViewerGrid.Children.Clear();
int index = listViewMarkingInfos.SelectedIndex;
ContainerMarkingDockPanel.DataContext = viewModels.markingManualStatuses[index];
TmcViewerGrid.Children.Add(viewModels.markingManualStatuses[index].Tmc); // this is not working
ContainerMarkingDockPanel.Visibility = Visibility.Visible;
}
3. Not worked. Way 2:
I have added
Grid property
in
MarkingManualStatuses
It sounds bad but anyway, I just wanted to see if it works or not.
In viewModel:
public class MarkingManualStatuses : INotifyPropertyChanged
{
public LaserLib Tmc { get; set; }
public Grid myGrid {get;set;}
public void TmcInitialize()
{
Tmc = new LaserLib ();
Tmc.Init();
myGrid = new Grid();
myGrid.Children.Add(Tmc);
}
}
In Manual_Page:
private void listViewMarkingInfos_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TmcViewerGrid.Children.Clear();
int index = listViewMarkingInfos.SelectedIndex;
ContainerMarkingDockPanel.DataContext = viewModels.markingManualStatuses[index];
TmcViewerGrid = viewModels.markingManualStatuses[index].myGrid; // this is also not working.
ContainerMarkingDockPanel.Visibility = Visibility.Visible;
}
I have tried two ways but it didn't work. Any suggestions to solve this?
And maybe I missed a small point that I forget, I am not sure, but Thank you all.

WPF calculated ViewModel and property changed notification

I stumbled on this problem a few days ago and nothing seems to give me a solution - or, at least, an idea. In my ViewModel A I have a calculated property that produces an ObservableCollection of ViewModels B. I can bind to this collection no problem, but changes in the properties of ViewModel B items don't show up in the UI. Any ideas will be greatly appreciated and don't hesitate to ask for any clarification or detail. Thanks in advance!
Update: Here's the property that doesn't notify
public Boolean IsHighlighted
{
get { return _IsHighlighted; }
set
{
if (_IsHighlighted != value)
{
_IsHighlighted = value;
OnPropertyChanged("IsHighlighted");
}
}
}
and the calculated property that produces the collection in ViewModel A
public ObservableCollection<PointViewModel> MidPoints
{
get
{
ObservableCollection<PointViewModel> midPoints = new ObservableCollection<PointViewModel>();
//
//....calculations
//
return midPoints;
}
}
Do not create a new ObservableCollection<PointViewModel> instance in the MidPoints property getter.
Instead, perform add and delete operations on the existing instance:
private readonly ObservableCollection<PointViewModel> midPoints
= new ObservableCollection<PointViewModel>();
public ObservableCollection<PointViewModel> MidPoints
{
get { return midPoints; }
}
public void UpdateMidPoints()
{
// performs calculations that add and remove elements to/from midPoints
// ...
}
In case it is for whatever reason required to create a new collection instance, you would have to raise the PropertyChanged event:
private ObservableCollection<PointViewModel> midPoints;
public ObservableCollection<PointViewModel> MidPoints
{
get { return midPoints; }
set
{
midPoints = value;
OnPropertyChanged("MidPoints");
}
}
public void UpdateMidPoints()
{
ObservableCollection<PointViewModel> newMidPoints
= new ObservableCollection<PointViewModel>();
//
// calculations...
//
MidPoints = newMidPoints;
}

c# - StackOverFlowException when mentioning ListBoxt?

I'm trying to get a new form (FormAlbum) to open when I click buttonOpenAlbum and have an item selected in the AlbumListBox.
If I just have this in buttonOpenAlbum_Click:
private void buttonOpenAlbum_Click(object sender, EventArgs e)
{
FormAlbum MusicForm = new FormAlbum(this);
MusicForm.ShowDialog();
}
The new from opens without error. However, as soon as I mention "AlbumListBox.SelectedItem" (as in the code belowin Form FormMain), I get a "StackOverflowException was unhandled" at:
public ListBox AlbumListBox
{
get
{ // <-This bracket here is where the error highlights
I don't understand why I'm getting this error, only that it must have something to do with AlbumListBox. What am I doing wrong? Any help is appreciated, thank you.
Form FormMain:
public FormMain()
{
InitializeComponent();
}
private void buttonAddAlbum_Click(object sender, EventArgs e)
{
FormAlbumAC addAlbumForm = new FormAlbumAC(this);
addAlbumForm.ShowDialog();
}
private void buttonOpenAlbum_Click(object sender, EventArgs e)
{
if (AlbumListBox.SelectedItem != null)
{
MessageBox.Show(AlbumListBox.SelectedItem.ToString());
FormAlbum MusicForm = new FormAlbum(this);
MusicForm.ShowDialog();
}
else
{
MessageBox.Show("You need to select an album from the list to open.");
}
}
public static class PublicVars
{
public static List<Album> AlbumList { get; set; }
static PublicVars()
{
AlbumList = new List<Album>(MAX_ALBUMS);
}
}
public ListBox AlbumListBox
{
get
{
return AlbumListBox;
}
}
Look at your property implementation:
public ListBox AlbumListBox
{
get
{
return AlbumListBox;
}
}
It's just calling itself, recursively. It may be easier to see that if we convert it to a method:
public ListBox GetAlbumListBox()
{
return GetAlbumListBox();
}
That's why you've got an overflow. It's not clear what you meant it to do... where did you expect the value to come from? You probably need a variable to back the property. What did you expect to set the value returned?
I'd also strongly discourage the design of the PublicVars class. Aside from the naming, you're basically using global variables - not a good idea. Work out which classes need access to the data, and how to get that data to them appropriately.

Passing Properties Between Winforms When Using Singleton

Ok so I am trying to pass a boolean from my Login form to my Home form, normally this would be fine for me and I would just use a property. However I thought I could use a similar method this time but I am implementing the singleton factory on the forms.
Here is the Login code relevant to this:
The AdminAccess property gets set fine and I have checked the value is correct.
private bool adminAccess;
public bool AdminAccess
{
get { return adminAccess; }
private set { adminAccess = value; }
}
private void btnLogin_Click(object sender, EventArgs e)
{
//Some Code Does Stuff
OpenHome();
}
private void OpenHome()
{
HomeForm CreateHomeForm = HomeForm.HomeUI;
CreateHomeForm.StartupHome = this;
//Trying to set the property.
CreateHomeForm.AdminPermissions= this.AdminAccess;
CreateHomeForm.Show();
this.Hide();
}
Here is the relevant code from the Home form:
public HomeForm()
{
InitializeComponent();
//just to check what is in the property quickly
textBox1.Text = AdminPermissions.ToString();
}
private bool adminPermissions;
public bool AdminPermissions
{
private get { return adminPermissions; }
set { adminPermissions = value; }
}
public Form StartupHome
{
set;
get;
}
private static HomeForm homeUI;
public static HomeForm HomeUI
{
get
{
if (homeUI == null || homeUI.IsDisposed)
{
homeUI = new HomeForm();
}
return homeUI;
}
}
The value gets reset when the HomeUI if loop runs as a new instance of the form is created. I can't seem to think how to modify this to get a working solution. As you can tell I am fairly amateur so I'm just looking for a quick and clean solution to this :)
Thank you very much for your time in advance!
You assign the value in the constructor, BEFORE the AdminPermissions property is actually set. Change your code like this
public class HomeForm
{
public HomeForm()
{
InitializeComponent();
}
private bool adminPermissions;
public bool AdminPermissions
{
get { return adminPermissions; }
set {
adminPermissions = value;
textBox1.Text = value.ToString();
}
}
...
}
Try setting the textBox1.Text value in one of the Form events. Try Loaded first, then Activated. You're resetting it to false every time in your constructor!

Infinite Loop error thrown when using property accessors in C#

I have a second window which opens when a certain staffname is searched for, this window prompts you to choose between the 2 staff members with the same name. The window then needs to return a value to the parent window to populate a datatemplate with relating data from the xml file.
I've tried to create a string which will be updated with a value depending on which button is clicked, this string can then be returned to the calling method on the first window and populate binding data in the Linq to Xml query.
But when it runs it causes a stackoverflow exception and that it could be an infinite loop. I'm not sure enough about c# to know what to change.
public partial class Choice : Window
{
private string StaffChoice;
public Choice()
{
InitializeComponent();
}
public string staffChoice
{
get { return this.StaffChoice; }
set { staffChoice = StaffChoice; }
}
private void btnMRG_Click(object sender, RoutedEventArgs e)
{
StaffChoice = "MRG";
this.Close();
}
private void btnRPG_Click(object sender, RoutedEventArgs e)
{
StaffChoice = "RPG";
this.Close();
}
}
Any help or suggestions would be great!
Thanks in advance!
Firstly, your naming conventions are wrong - the field should be called staffChoice and the property should be called StaffChoice. Please read the .NET naming conventions for more information. However, now look at your property closely:
public string staffChoice
{
get { return this.StaffChoice; }
set { staffChoice = StaffChoice; }
}
What do you think the setter does? There are two problems with it:
It ignores the value that you're trying to set it to.
It calls itself recursively.
You could fix this by keeping the manually-declared field, fixing the naming conventions, and changing the property to set the variable to value like this:
private string staffChoice;
public string StaffChoice
{
get { return staffChoice; }
set { staffChoice = value; }
}
However, it would be simpler to use an automatically implemented property:
public string StaffChoice { get; set; }
This will create the backing field and the getter/setter for you automatically.
The simplest way is to declare a property like this...
public string StaffChoice { get; set; }
your problem is you are basically calling the property setter from within the same setter - thus you have a recursive loop. You could change your code like this to make it work...
private string StaffChoice;
public string staffChoice
{
get { return this.StaffChoice; }
set { StaffChoice = value; }
}
Your setter isn't right, you are assigning a value to itself (causing the infinite loop) and not using value.
You should change your code to this, your naming convention looked backwards so I corrected it, hope you don't mind:
private string staffChoice;
public Choice()
{
InitializeComponent();
}
public string StaffChoice
{
get { return staffChoice; }
set { staffChoice = value; }
}
private void btnMRG_Click(object sender, RoutedEventArgs e)
{
staffChoice = "MRG";
this.Close();
}
private void btnRPG_Click(object sender, RoutedEventArgs e)
{
staffChoice = "RPG";
this.Close();
}
Your property should be:
public string staffChoice
{
get { return this.StaffChoice; }
set { this.StaffChoice = value; }
}
In your code you are calling the setter again in the setter - hence the infinite recursion.
However, as you are not doing anything special in the setter (like notifying the UI that the property has changed you could simply have:
public string staffChoice { get; set; }
This "auto property" is a little cleaner.
(BTW: the normal practice is to have the back variable starting with a lower case letter and the public property starting with an upper case one. However, if you are consistent in your application it doesn't really matter.)

Categories

Resources