I have created a C# User Control in Visual Studio 2019. It has a property called "BoundLayout".
public Layout BoundLayout
{
get
{
return _Layout;
}
set
{
_Layout = value as Layout;
if (_Layout == null)
{
MessageBox.Show("Value submitted is not of type 'LAYOUT'","Invalid Value",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
else
{
InitializeControl();
}
}
}
If a program attempts to assign an incompatible value to the property an error message is displayed in a MessageBox. This works correctly.
What is very strange is that when ever I BUILD (not RUN) the project this error message is displayed in its modal MessageBox which must be acknowledged before you can return to Visual Studio. This occurs when building in both Debug and Release modes. A break point added to the property set code does not get triggered. The build completes successfully without errors or warnings and I can run the application.
The application, including this User Control operates as intended. I have never encountered this behavior before. Has anyone else?
The complete (still in development) code for the User Control:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Dispatcher
{
public partial class DivisionModuleGrid : UserControl
{
private Layout _Layout = null;
private ObservableListSource<LayoutDivision> _LayoutDivisions;
private DivisionModulesList _activeDivision = null;
private int _divisionCount;
public Layout BoundLayout
{
get
{
return _Layout;
}
set
{
_Layout = value as Layout;
if (_Layout == null)
{
MessageBox.Show("Value submitted is not of type 'LAYOUT'","Invalid Value",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
else
{
InitializeControl();
}
}
}
public DivisionModulesList ActiveDivision
{
get
{
return _activeDivision;
}
set
{
_activeDivision = value as DivisionModulesList;
if (_activeDivision != null)
{
lbl_ActiveDivision.Text = _activeDivision.DivisionName;
}
else
{
lbl_ActiveDivision.Text = "-No Active Division-";
}
}
}
public DivisionModuleGrid()
{
InitializeComponent();
}
private void InitializeControl()
{
_LayoutDivisions = _Layout.LayoutDivisions;
_divisionCount = _LayoutDivisions.Count;
tbx_LayoutName.Text = _Layout.LayoutName;
// Grid Layout divide into Rows & Columns
int tlp_rows = _divisionCount / 3;
TableLayoutPanel tlp = (TableLayoutPanel)(Controls.Find("tlp_DivisionGrid", false)[0]);
DivisionModulesList dml;
foreach (LayoutDivision ld in _LayoutDivisions)
{
dml = new DivisionModulesList(ld);
dml.BoundDivision = ld;
tlp.Controls.Add(dml);
}
}
private void Tlp_DivisionGrid_Paint(object sender, PaintEventArgs e)
{
}
}
}
When creating your own Properties for a UserControl, and you use this UserControl at another place, the Designer is generating Code for this property like:
yourControl.BoundLayout = null;
Search for it in the Designer.cs file; it will solve the problem until the code is regenerated.
If the designer displays your control it runs your code, and displays your MessageBox at Design-Time (not Build or Runtime).
Do avoid this forever put
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
public Layout BoundLayout
on all your properties, if you intend to modify it by code only and not within the designer, especially if null is an invalid value.
When using your UserControl again at another place it will not create this BoundLayout=null again any more, but for existing references to your UserControl, you have to remove this line manually.
General Advice: In general, you should throw an exception rather than showing a MessageBox. But it has nothing to do with the problem.
You have defined the initial value of the property as null. This means, when you drop an instance of the control on the form, it will serialize the null assignment as well and generates a code like this:
userControl1.Name = "userControl1";
userControl1.Size = new Size( 100, 100);
userControl1.SomeProperty = null;
...
To solve the problem, you can use either of the following options:
Prevent designer from serializing the value if it's null.
Disable the validation at design time.
Prevent designer from serializing the value always. (as also proposed by Holger)
Example 1 - Prevent designer from serializing the value if it's null
You can set the default value of the property as null using DefaultValue attribute. Then it will not be serialized by designer when the value of the property is null when you drop the control on the form or when assign null value to the property at design time.
private SomeType someProperty = null;
[DefaultValue(null)]
public SomeType SomeProperty
{
get { return someProperty; }
set
{
if (value == null)
MessageBox.Show("Why null????");
else
someProperty = value;
}
}
Example 2 - Disable the validation at design time
You can check if the control is in DesignMode then stop validation:
private SomeType someProperty = null;
public SomeType SomeProperty
{
get { return someProperty; }
set
{
if (value == null && !DesignMode)
MessageBox.Show("Why null????");
else
someProperty = value;
}
}
Related
Recently I've been learning C# and WPF for work. I'm trying to use MVVM on a project I'm working on, just to keep the code organized and learn how it works.
In MVVM, controls on the View bind to properties on the ViewModel, which implements INotifyPropertyChanged. Pretty often, when a certain property is updated, I'll want a bunch of other properties to get updated as a result.
For example, I have a ListBox with a TextBox above it. You can type in the TextBox, and it filters the stuff in the ListBox. But I also need to be able to clear the TextBox from code in certain cases. The code ends up looking like this:
private Collection<string> _listOfStuff;
public Collection<string> FilteredList
{
get
{
if (String.IsNullOrWhiteSpace(SearchText))
{
return _listOfStuff;
}
else
{
return new Collection<string>(_listOfStuff.Where(x => x.Contains(SearchText)));
}
}
set
{
if (value != _listOfStuff)
{
_listOfStuff = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText"); // Tells the view to change the value of the TextBox
OnPropertyChanged("FilteredList"); // Tells the view to update the filtered list
}
}
}
As this project gets bigger, this is starting to feel sloppy. I have one setter with 6 calls to OnPropertyChanged and it's getting hard to keep track of stuff. Is there a better way to do this?
I tried out Assisticant on a project about a year ago. It figures out which of your properties need to raise notifications and also which are related. There is a good course for it on Pluralsight and the examples on the website are pretty good. If nothing else you could check out the source code to see how he did it.
Also some good suggestions from Change Notification in MVVM Hierarchies.
They mentioned:
Use an attribute -> e.g. [DependsUpon(nameof(Size))]
and
Josh Smith's PropertyObserver
Could put the raise property change calls in a method if you just need to raise the same notifications every time.
First you shouldn't do potentially expensive operations in a command, then you'll be able to remove the OnPropertyChanged("FilteredList"); from your SearchText.
So you should move that code from the getter and into it's own command and bind it from XAML (either as Command on a button or using Blends Interactivity Trigger to call it when the text fields value changes).
public ICommand SearchCommand { get; protected set; }
// Constructor
public MyViewModel()
{
// DelegateCommand.FromAsyncHandler is from Prism Framework, but you can use
// whatever your MVVM framework offers for async commands
SearchCommand = DelegateCommand.FromAsyncHandler(DoSearch);
}
public async Task DoSearch()
{
var result = await _listOfStuff.Where(x => x.Contains(SearchText)).ToListAsync();
FilteredList = new Collection<string>(result);
}
private Collection<string> _listOfStuff;
private Collection<string> _filteredList;
public Collection<string> FilteredList
{
get
{
return _filteredList;
}
set
{
if (value != _filteredList)
{
_filteredList = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get
{
return _searchText;
}
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText");
}
}
}
On a side note: You can also use OnPropertyChanged(nameof(FilteredList)); to have a refactor friendly version, when you rename your property all of your OnPropertyChanged calls will be updated to. Requires C# 6.0 though, but it's compatible with older .NET Frameworks (back to 2.0), but requires Visual Studio 2015 or later
For anyone searching for a good solution to this type of problem: Check out ReactiveUI.
It is a framework based on Reactive Extensions (Rx), with the idea that you model this type of dependencies between properties explicitly, without a jungle of RaisePropertyChanged(..).
Specifically check out the ObservableAsPropertyHelper (sometimes called OAPH).
You should only raise OnPropertyChanged in the setter of the property itself.
A cleaner implementation of your ViewModel can be:
private Collection<string> _listOfStuff;
private Collection<string> _filteredList;
public Collection<string> FilteredList
{
get
{
return _filteredList;
}
set
{
if (value != _filteredList)
{
_filteredList = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText");
FilteredList = new Collection<string>(_listOfStuff.Where(x => x.Contains(SearchText)));
}
}
}
if you just don't wanna type only other option is to fire OnPropertyChanged for all properties which can be done by passing a null or string.Empty, although it will be sloppier code!
OnPropertyChanged(Null);
or
OnPropertyChanged(String.Empty);
I am creating an application that uses several threads as a result I want to try to use UIControls in my code behind as few as possible. The way I do it is by binding the controls to a property in my code behind that way I will be able to update the control by changing that property it does not matter if that property is updated on a different thread. Anyways I am creating the following code in order for the class to create the bindings form me.
public static class MyExtensionMethods
{
public static TextBoxBind<T> BindTextBox<T>(this TextBox textbox, string property=null)
{
return new TextBoxBind<T>(textbox,property);
}
}
public class TextBoxBind<T> : INotifyPropertyChanged
{
string property;
protected T _Value;
public T Value
{
get { return _Value; }
set { _Value = value; OnPropertyChanged(property); }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged(string propertyName){
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public TextBoxBind(TextBox textbox, string property)
{
if (property == null)
{
property = "Value";
}
this.property = property;
Binding b = new Binding(property)
{
Source = this
};
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
textbox.SetBinding(TextBox.TextProperty, b);
}
}
And on my XAML I have:
<TextBox Name="textBox2" />
Therefore I will be able to use the first code that I posted as:
var newTextBox2 = textBox2.BindTextBox<int>();
newTextBox2.Value = 50; // this will update the textBox2.Text = "2"
// also every time I update the value of textBox2 newTextBox2.Value will update as well
The problem is when I try to bind it to a custom object. Take this code for example:
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
public override string ToString()
{
return Age.ToString();
}
}
void LogIn_Loaded(object sender, RoutedEventArgs e)
{
txtUsuario.Focus();
var newTextBox2 = textBox2.BindTextBox<Person>("Age");
// here newTextBox2 never updates....
}
When it comes to data binding one should update an object (doesn't matter CLR property or DependencyObject) from the same thread, as the UI is running at. If you have a UI element bound to something in code, updating that from a separate thread will lead to exception. However, you can always retrieve your UI thread and perform property update there.
Here's a piece of code, that I am using in a similar situation as you have:
ThreadStart updateLogs = delegate()
{
ObservableCollection<LogMessage> newLogs = this._parcer.Parce();
foreach (LogMessage log in newLogs)
LogMessages.Add(log);
};
App.Current.Dispatcher.BeginInvoke(updateLogs, null);
This block of code is running in a thread different to one UI is running at. So I extract the code, that actually updates the binding source (which is LogMessages) into a delegate updateLogs and then run this delegate in a UI thread, passing it to the application dispatcher.
Nevertheless, WPF application can have more than one Dispather if, for example, you create separate windows in separate threads, although this approach is rare. But just in case, DependencyObject class has a Dispatcher property, which references the Dispather that owns this object.
OnPropertyChanged(property); should be pointing to Value, since that's the Name of your Property.
This should not be pointing to the type T.
So this code is not right:
if (property == null)
{
property = "Value";
}
because property should always be "Value"
public T Value
{
get { return _Value; }
set { _Value = value; OnPropertyChanged("Value"); }
}
I am trying to incorporate a property grid control with a class that has a list/collection of another class as one of the properties. Lets call them class A and the list would be containing class B for reference.
I was wanting to incorporate a form that had two list boxes. The list box on the left would contain a list of all of class B's in my program that are not currently in the list on the right. The list on the right would contain all of the class B's that are currently associated with class A. I want buttons in between to move items between the two lists.
This would be easy to design, but I'm not sure exactly how to set up the form to be used as the collection editor.
Can anyone point me in the right direction?
And also, how can I go about having setting up a drop down for a property that contains a list of id's to select from if anyone could point me in the direction for accomplishing this as well.
Okay, I was finally able to track down how to accomplish this.
I was attempting to create a custom CollectionEditor.CollectionForm which was close to what I needed to do, but it wasn't quite the right direction.
First of all, create a regular Windows Form which includes your GUI for editing your collection. Then just include button/buttons which return a DialogResult in the Form.
Now the key to accomplishing what I was looking for is not a CollectionEditor.CollectionForm as I had thought would be the correct approach, but rather a UITypeEditor.
So, I created a class that inherited from the UITypeEditor. Then you simply flesh it out as such:
public class CustomCollectionModalEditor: UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
if (context ==null || context.Instance == null)
return base.GetEditStyle(context);
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService editorService;
if (context == null || context.Instance == null || provider == null)
return value;
editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
CForm CollectionEditor = new CForm();
if (editorService.ShowDialog(CollectionEditor) == System.Windows.Forms.DialogResult.OK)
return CollectionEditor.Programmed;
return value;
//return base.EditValue(context, provider, value);
}
}
The key parts to take note of, are the functions GetEditStyle and EditValue. The part responsible for firing-off the Form you created to edit your collection, is in the EditValue override function.
CForm is the custom collection editor form I designed in this test to edit the collection. You need to fetch the IWindowsFormsEditorService associated with the IServiceProvider and simply call the .ShowDialog(formVariable) of the IWindowsFormsEditorService in order to show the form you designed to edit the collection. You can then catch the returned DialogResult value from your Form and perform any custom handling that you require.
I hope this helps someone out as it took me quite a bit of digging to determine the right way to incorporate this.
This answers Brandon's question. I too searched long and hard on how to actually replace the default propertygrid collection editor. Nathan's answer was the final solution. Brandon here is how I was able to use Nathan's solution and use my own collection editor.
using Swfd = System.Windows.Forms.Design;
using Scm = System.ComponentModel;
using Sdd = System.Drawing.Design;
public class CustomCollectionModalEditor : Sdd.UITypeEditor
{
public override Sdd.UITypeEditorEditStyle GetEditStyle(Scm.ITypeDescriptorContext context)
{
if (context == null || context.Instance == null)
return base.GetEditStyle(context);
return Sdd.UITypeEditorEditStyle.Modal;
}
public override object EditValue(Scm.ITypeDescriptorContext context, IServiceProvider provider, object value)
{
Swfd.IWindowsFormsEditorService editorService;
if (context == null || context.Instance == null || provider == null)
return value;
editorService = (Swfd.IWindowsFormsEditorService)provider.GetService(typeof(Swfd.IWindowsFormsEditorService));
//CForm CollectionEditor = new CForm();
//--- Replaced the Collection from this post with mine which requires an argument that passes the collection
Ccne.CustomCollection editgcp = new Ccne.CustomCollection(); // Ccne.CustomCollection is my collection
editgcp = MYGCPS; // MYGCPS is the actual instance to be edited
Gcp_Editor.GcpEditorMain CollectionEditor = new Gcp_Editor.GcpEditorMain(editgcp); // call my editor
if (editorService.ShowDialog(CollectionEditor) == System.Windows.Forms.DialogResult.OK)
{
MYGCPS = CollectionEditor.ReturnValue1; // update current instance of the collection with the returned edited collection
THISPG.Refresh(); // calls a method which refreshes the property grid
return value; // the replaces the statment in the post >> CollectionEditor.Programmed;
}
//---
return value;
//return base.EditValue(context, provider, value);
}
}
//---------- The propertygrid entry
private Ccne.CustomCollection gCPs;
[Scm.Category("3 - Optional inputs to run gdal_translate")]
[PropertyOrder(133)]
[Scm.TypeConverter(typeof(Ccne.CustomCollectionConverter))]
[Scm.Editor(typeof(CustomCollectionModalEditor), typeof(Sdd.UITypeEditor))]
[Scm.Description("The Collection of the single or multiple Ground Control Points (Gcp)" +
" entered. \n Each gcp requires a Name, pixel, line, easting, " +
"northing, and optionally an elevation")]
[Scm.RefreshProperties(Scm.RefreshProperties.All)] // http://stackoverflow.com/questions/3120496/updating-a-propertygrid
[Scm.DisplayName("23 Collection of Gcp's")]
[Scm.ReadOnly(true)] // prevents values being changed without validation provided by form
public Ccne.CustomCollection GCPs
{
get { return gCPs; }
set { gCPs = value; }
}
//-------- important code from CollectionEditor i.e. > Gcp_Editor.GcpEditorMain(editgcp)
using Swf = System.Windows.Forms;
namespace Gcp_Editor
{
public partial class GcpEditorMain : Swf.Form
{
public Ccne.CustomCollection ReturnValue1 { get; set; }
...
public GcpEditorMain(Ccne.CustomCollection input1)
{
InitializeComponent();
formcollection = input1;
}
...
private void OkayBtn_Click(object sender, EventArgs e)
{
this.DialogResult = Swf.DialogResult.OK;
ReturnValue1 = formcollection;
return;
}
As the topic suggests I have some problems with PropertyInfo.SetValue. To get to the point, here is my example - I have created my own class and the main thing about it is the presentation object:
using System;
using System.Reflection;
namespace TestingSetValue
{
public class Link
{
private object presentationObject = null;
private string captionInternal = string.Empty;
public Link (string caption)
{
captionInternal = caption;
}
public string CaptionInternal
{
get { return captionInternal; }
set { captionInternal = value; }
}
public bool Visible
{
get
{
if (PresentationObject != null)
{
PropertyInfo pi = PresentationObject.GetType().GetProperty("Visible");
if (pi != null)
{
return Convert.ToBoolean(pi.GetValue(PresentationObject, null));
}
}
return true;
}
set
{
if (PresentationObject != null)
{
PropertyInfo pi = PresentationObject.GetType().GetProperty("Visible");
if (pi != null)
{
pi.SetValue(PresentationObject, (bool)value, null);
}
}
}
}
public object PresentationObject
{
get { return presentationObject; }
set { presentationObject = value; }
}
}
}
Then, I do this:
private void btnShowLink_Click(object sender, EventArgs e)
{
Link link = new Link("Here I am!");
this.contextMenu.Items.Clear();
this.contextMenu.Items.Add(link.CaptionInternal);
link.PresentationObject = this.contextMenu.Items[0];
link.Visible = true;
lblCurrentVisibility.Text = link.Visible.ToString();
}
Now, I can imagine this doesn't look too logical/ economical, but it shows the essence of my real problem. Namely, why doesn't the visibility of presentation object (and the value of link.Visible) change, after I call:
link.Visible = true;
I simply do not know what else to do to make this work... Any help is deeply appreciated.
To make things even more interesting, the property Enabled behaves as expected of it...
PropertyInfo pi = PresentationObject.GetType().GetProperty("Enabled");
Could it be related to the fact that Visible is actually a property of ToolStripDropDownItem base base object, whereas Enabled is 'direct' property of ToolStripDropDownItem ?
It would have been easier to figure this out if you said upfront what class this is but now we know it is ToolStripDropDownItem which we can infer means WinForms.
What you are seeing is an oddity with the ToolStripItem's Visible property. It's setter & getter are not tied directly together. MSDN says
"The Available property is different from the Visible property in that
Available indicates whether the ToolStripItem is shown, while Visible
indicates whether the ToolStripItem and its parent are shown. Setting
either Available or Visible to true or false sets the other property
to true or false."
In other words, you want to use the Available property instead of the Visible property
Check http://msdn.microsoft.com/en-us/library/system.web.ui.control.visible.aspx. Maybe this is causing your problem.
There is very important piece of info:
If this property is false, the server control is not rendered. You should take this into account when organizing the layout of your page. If a container control is not rendered, any controls that it contains will not be rendered even if you set the Visible property of an individual control to true. In that case, the individual control returns false for the Visible property even if you have explicitly set it to true. (That is, if the Visible property of the parent control is set to false, the child control inherits that setting and the setting takes precedence over any local setting.)
I have been trying to build a user control with some custom properties set in the designer. However the control involves some interop code and settings which shouldn't be adjusted at runtime. Is there a way to stop the values being changed after they have been initially set by the designer code?
Are you able to modify the property definition? One approach is, add a sentinel to the property setter, and allow only one set operation (which would usually be done by InitializeComponent()):
private int _myProperty;
private bool _isMyPropertySet = false;
public int MyProperty
{
set
{
if (!_isMyPropertySet)
{
_isMyPropertySet = true;
_myProperty = value;
}
else
{
throw new NotSupportedException();
}
}
}
Michael provided a great answer, and it will solve your problem at runtime. However, at design time, if you need to be able to change that value more than once (it is design time, and the probability is likely high), then you will want to combine the DesignMode check with Michaels example:
private int _myProperty;
private bool _isMyPropertySet = false;
public int MyProperty
{
set
{
if (this.DesignMode || !_isMyPropertySet)
{
_isMyPropertySet = true;
_myProperty = value;
}
else
{
throw new NotSupportedException();
}
}
}
Now you will be able to edit this value to your hearts content during design, without running into that NotSupportedException() and getting a botched designer on the second set.
You could throw an exception inside the property setter?
public int SomeProperty {
set {
if(designerComplete) {
throw new IllegalOperationException();
}
}
}
Set designerComplete as a class variable - set it to true after the InitializeComponent method is called in the constructor.
The WinForms architecture provides a built-in way to test whether code is currently being executed in design mode - the Component.DesignMode property.
So you probably want an implementation something like this:
private int _foo;
public int Foo
{
get { return _foo; }
set
{
if (this.DesignMode)
throw new InvalidOperationException();
_foo = value;
}
}