There are default values you can set for some of window properties in WPF. For example you can set a Canvas' Width or a TextBlock's Text in XAML and then your app can change them by accessing the controls. Are there any ways of setting those values to how they were declared in XAML without saving them yourself? Are they kept somewhere so that you can access them at runtime?
My question is if there is maybe a function or some hacky way of setting all the values to default somewhere in the wpf api?
No, there isn't.
You better close the window and create a new one. Or you will have to "manually" reset all values by writing your own custom code that does this.
The framework cannot be supposed to know when you want to reset the values of your control properties without you specifying it somewhere. And the way to specify it is to write code.
I think that there is no way for WPF to know what your default values are but you can create your own attribute that contains information about default value and create method that will set that value.
First we need attribute that will hold information about default value of given property:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
class DefaultAttribute : Attribute
{
public object Value;
public DefaultAttribute(object value)
{
this.Value = value;
}
}
Given this attribute we can easy declare default values for our properties like this:
class ViewModel
{
[Default("Noname")]
public string Name { get; set; }
public int Age { get; set; }
}
Then if you want to apply defaults to your view model you need some kind of method that will do that for you:
private static void ApplyDefaults(object model)
{
PropertyInfo[] properties = model.GetType().GetProperties();
foreach (var property in properties)
{
DefaultAttribute defaultAttr = property.GetCustomAttribute<DefaultAttribute>();
if (defaultAttr != null)
property.SetValue(model, defaultAttr.Value);
}
}
Example program with usage:
public static void Main()
{
var vm = new ViewModel();
Console.WriteLine($"Start - {vm}");
ApplyDefaults(vm);
Console.WriteLine($"ApplyDefaults - {vm}");
vm.Name = "Damian";
vm.Age = 23;
Console.WriteLine($"After using setters - {vm}");
ApplyDefaults(vm);
Console.WriteLine($"After ApplyDefaults - {vm}");
}
Output for the program is:
Start - - 0
ApplyDefaults - Noname - 0
After using setters - Damian - 23
After ApplyDefaults - Noname - 23
I've just checked that .NET have DefaultValue Attribute which you can use in similar way.
Related
i have this class
public class Property{
public string Name {get;set;}
public object Value {get;set;}
}
i want to create list of the above class List<Property> and dynamically add Mark Up Controls Code only
, so as their website they have an example HERE and what i did to that example is adding a public property of type Property to the TextBoxWithLabel class and changed the setter of the above example for binding as follows:
[MarkupOptions(AllowHardCodedValue = false)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set {
SetValue(TextProperty, value);
Property.Value = value;
}
}
public static readonly DotvvmProperty TextProperty
= DotvvmProperty.Register<string, TextBoxWithLabel>(c => c.Text, "");
when i run the app and type something in the input field, the Value property of Type Property still null and here is where i'm stuck
i tried also to debug setter and it turns out it does not reach there so there is problem with run-time binding, which is 'as their example' this line of code
textBox.SetBinding(TextBox.TextProperty, GetValueBinding(TextProperty));
any help will appreciated :)
EDIT:
for more clarification,i have page called MainAppPage
and Markup Control with code behind called ContentControl
simply , MainAppPage passes List<Property> to ContentControl using this in MainAppPage
<controls:ContentControl Instance="{value: ClassObject}"/> then ContentControl start iterating through List<Property> and creating InputField's that derive from HtmlGenericControl
InputField's rendering like a charm in ContentControl
only thing is not working is binding , so again, how to bind Property.Value to InputField.Text so any changes happens in UI from user reflects on Property.Value after the InputField gets unfocused like any other MVVM pattern ?
DotVVM does not assign to the property usning the setter, is sets the underlying property store in DotvvmBindableObject instead. It's very simmilar what WPF does with their DependencyProperty, it's needed to represent the data bindings. You can actually completely omit the C# property declaration, declaring the field TextProperty and calling the DotvvmProperty.Register is enough to declare a property for dotvvm.
Other "problem" is that the controls do not store any data, everything has to be persisted in the view model. You can only use the control properties to data-bind a view model property. I think we are running here into a XY problem, I can only tell why your code does not work, but I have no idea what are actually trying to do...
Anyway, if you just want to "bind" your control to a view model property, have a look at https://www.dotvvm.com/docs/tutorials/control-development-markup-controls-with-code/2.0. You can declare the property like that:
[MarkupOptions(AllowHardCodedValue = false)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DotvvmProperty TextProperty
= DotvvmProperty.Register<string, TextBoxWithLabel>(c => c.Text, "");
Use it in the markup of you control
#baseType FullName.Of.YourControl
{{value: _control.Text}}
And use the control in your page (or other control)
<cc:YourControl Text="{value: _this.Property.Value}" />
I'm working in a WPF project, it has a numerous number of user control.
for some forms , when the user close the container tab, a confirmation close is shown up, for other forms they are just closed.
so I use the
FrmAccounts FrmAcc = new FrmAccounts {Tag = "showConfirmClose"};
to decide what tab I need to close.
and in the closing event I check if the tag is set to showConfirmClose to show the confirmation message. But I don't like using Tag it's not good in C#, also I thought, what if I want to send more data (the only solution will be to comma separating them in the Tag and Split them, but this is worse).
I can't find a good, performant way to accomplish such task:
if this is possible:
FrmAccounts FrmAcc = new FrmAccounts {new{ShowConfirmClose= true }};
Attached property
You can use an AttachedProperty to store the additional information for your objects. However, these object have to be DependencyObjects, otherwise it won't work.
Here is an example.
Define your attached property in a separate class (it can be a simple static class or a DependencyObject):
namespace YourNamespace
{
public static class CloseConfirmation
{
public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached(
"IsActive",
typeof(bool),
typeof(CloseConfirmation));
public static bool GetIsActive(DependencyObject obj)
{
return (bool)obj.GetValue(HiddenProperty);
}
public static void SetIsActive(DependencyObject obj, bool value)
{
obj.SetValue(HiddenProperty, value);
}
}
}
In XAML, set the attached property value for your objects (WPF Windows, Pages and other DependencyObjects):
<FrmAccounts xmlns:yns="YourNamespace"
yns:CloseConfirmation.IsActive="true">
<!-- Content -->
</FrmAccounts>
You can use this attached property in your XAML definition, e.g. in triggers.
You can also access its value in code-behind:
FrmAccounts frm = new FrmAccounts();
CloseConfirmation.SetIsActive(frm, true);
var isActive = CloseConfirmation.GetIsActive(frm); // true
You can create an attached property not only for a simple type like bool or int, but also for any custom type. So use your own container class if you need to transfer a lot of data.
Tag object
Using Tags is fully OK in .NET. Virtually any UI type has this property, so that you can attach objects of any type to your UI elements. However, these types cannot be anonymous types. You have to create your own container type:
class Container
{
public bool IsConfirmationNeeded { get; }
public IEnumerable<IItem> MyData { get; } // any data you need to pass
}
FrmAccounts frm = new FrmAccounts();
frm.Tag = new Container();
var container = (Container)frm.Tag;
var confirmation = container.IsConfirmationNeeded; // false by default
I am working on some custom (inherited) controls with custom properties for which I have added design-time support after proper attribute decoration.
All works well but my problem is that the auto-generated code in the *.Designer.cs file where the control is used, has a particular order that it sets the various properties in (both base and new properties). This order looks like it is alphabetical w.r.t. the property name.
So the auto generated code looks like this:
//
// myTabPage1
//
this.myTabPage1.Identifier = "ID";
this.myTabPage1.Name = "myTabPage1";
this.myTabPage1.Size = new System.Drawing.Size(294, 272);
this.myTabPage1.Text = "TTT";
Where I would have liked it to look like this:
//
// myTabPage1
//
this.myTabPage1.Name = "myTabPage1";
this.myTabPage1.Size = new System.Drawing.Size(294, 272);
this.myTabPage1.Text = "TTT";
this.myTabPage1.Identifier = "ID";
The reason I need this is because setting the Identifier property affects Text which is then reverted back to its design-time value, annulling the effect of setting the Identifier.
There are of course simple workarounds (the simplest of which is not to set the Text property which work well) but it would be nice if this was not a design-time "worry", as there is extensive usage of this design pattern in many inherited control types and their instances.
It is also helpful to set Text to identify the controls on the form designer (Identifier has no effect on Text at design-time).
No, you can't affect serialization order. This is otherwise a common problem with a general solution, implement the ISupportInitialize interface. Your BeginInit() method is called just before the designer starts assigning properties, you'd typically set a bool variable that ensures that property setters don't have unintended side-effects. Your EndInit() method is called when it is done and all properties have a value, you'd set the variable back to false and do whatever you need to do to use the values.
Your question isn't specific enough about the control with the problem, but a possible implementation could look like this:
public partial class CustomControl : UserControl, ISupportInitialize {
public CustomControl() {
InitializeComponent();
}
private bool initializing;
private string id = "";
public string ID {
get { return id; }
set { id = value;
if (!initializing) label1.Text = value;
}
}
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text {
get { return base.Text; }
set {
base.Text = value;
if (!initializing && !this.DesignMode) label1.Text = value;
}
}
public void BeginInit() {
initializing = true;
}
public void EndInit() {
initializing = false;
label1.Text = ID;
}
}
Also note the [DesignerSerializationVisibility] attribute in this code, when you use Hidden then the property doesn't get serialized at all. That could be a simple solution to your problem.
i am trying to use Property Editor for my user control but it doesn't work.
if i set the property in the form load if works, but if i want to use the property editor it don't save my changes (when i click again in the property editor it comes clear)
this is how i define the property in my user control:
private List<Field> _searchField;
public List<Field> SearchField
{
get { return _searchField ?? (_searchField = new List<Field>()); }
}
You need to apply DesignerSerializationVisibility attribute to your property with DesignerSerializationVisibility.Content.
This tells the code generator to produces code for the contents of the object, rather than for the object itself. It helps in code generation for types other than primitive types.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<int> SearchField { get { return _searchField ?? (_searchField = new List<int>()); } }
Today at work, I stumbled upon a problem that was driving me nuts.
Basically my goal is this:
I have a UserControl1, with a field of the type Collection<Class1> and a corresponding property Collection<Class1> Prop. Like this:
public class UserControl1 : UserControl
{
private Collection<Class1> field = null;
// later changed to:
//private Collection<Class1> field = new Collection<Class1>();
[Category("Data")]
[DefaultValue(null)]
[Description("asdf")]
public Collection<Class1> prop
{
get { return field; }
set { field = value; }
}
}
// later added:
//[Serializable]
public class Class1
{
private bool booltest; public bool Booltest { get...set...}
private int inttest; public int Inttest { get...set...}
}
If you already know what I screwed up: no need to read the rest. I am going to describe what exactly I did.
Now I put the UserControl onto a random Form and change the Prop property. A generic "Collection Editor" appears, like the one used for the columns and groups in a listview control. I can enter data as expected. However, when I click OK, the data is gone.
It took me over hour to figure out that I actually have to instantiate my field: private Collection<MyClass> field = new Collection<MyClass>();. Very good, only that the designer entered superspazzing mode. Cascading nightmare error message that can be reduced to: "You must put [Serializable] before your Class1." After doing that I could actually put my UserControl1 on the Form again.
But that only works once. When opening the designer of the Form where I use the UserControl1 after editing something, it gives me an error:
Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'.
Well. The Error List says:
Warning: ResX file Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'. Line 134, position 5. cannot be parsed.
The designer tries to fetch the Property's data from the resx file. Removing the resx file "solves" that exactly once.
The Form can now be displayed again, with my UserControl1. The Collection property is editable, and it is being saved. It actually works. Once. Whenever I change something and then try to open the Form's designer again, the above error occurs again. I can delete the resx file, but that will of course also delete my data.
Relevant resources that helped me so far (among a ton of not so helpful search results):
http://www.codeproject.com/Answers/190675/Usercontrol-with-custom-class-property#answer1
http://www.codeproject.com/KB/cs/propertyeditor.aspx
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=94
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx
(I also tried implementing ISerializable and overriding GetObjectData with
{ info.AddValue("testbool", testbool); info.AddValue("testint", testint); }
didn't help either (I also tried the property names instead of the field names))
Sorry for writing this like a bad horror novel btw.
What you want is a design time support with CodeDom serialization. You do not need SerializableAttribute or ISerializable, those are for binary serialization.
Since you want to serialize the collection, you must tell the designer to serialize it as such. That is done with the DesignerSerializationVisibiliby attribute - value of Content tells the designer to serialize property contents rather than property itself. Contents of the property should of course be CodeDom serializable, which simple classes with simple properties are by default.
So if you change your UserControl1 class like this:
public class UserControl1 : UserControl
{
private Collection<Class1> field = new Collection<Class1>();
[Category("Data")]
[Description("asdf")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Collection<Class1> prop
{
get { return field; }
}
}
... it should do the trick. Oh and collection properties are usually not writeable, although that is not mandatory. But serializer expects the collection property to be initialized, that is why you had to add initialization for the field.
Another note, if you do not want that your property is marked with bold in the property editor, you can specify a more complex "default value" through a special method ShouldSerializePropertyName, which can even be private. Like so:
private bool ShouldSerializeprop()
{
return (field.Count > 0);
}
Now your property will only be bold when it is not empty. But I digress, this was not a question :)
The perfect exemple is this:
public partial class SCon : UserControl
{
public SCon()
{
InitializeComponent();
if (Persoanas == null)
{
Persoanas = new List<Persoana>();
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Persoan> Persoanas { get; set; }
}
[Serializable]
public class Persoan
{
public int Id { get; set; }
public String Name { get; set; }
}
Just change Collection<> to List<>