Is there an easy way to programatically setting a property value on a control such that it will be persisted in the designer-generated code?
I imagine a piece of code in the control constructor or load event which is executed when i open the control in design mode, but sets a property such that it will be persisted the same way as if I changed the value manually through the properties grid.
Edit: Yes, this would be the same as editing the designer code manually, but I want to do it programatically.
Presuming I understand the question
You can databind that property to a setting, using the Visual studio Gui. Check the properties for that control, under the Data section for (Application Settings), (Property Bindings).
It depends on what kind of functionality you want. If you only need the properties to be set when you add the control to a form, then setting the properties in the control's constructor works perfectly. However, changes you make using the Properties panel will take precedence, and setting properties in the control's constructor won't necessarily affect existing instances of the control.
If you want to be able to change the properties for instances of the control in one place, assigning bindings in (application settings), (property bindings) works. Then you can modify all the bindings from the Settings.settings file. This still requires you to assign property bindings for each instance of the control, though.
Now for the finale. If you want to set properties in the control's class that affect all instances of the control, whether the instances are yet to be created or already exist, you have to get a little creative. I found a solution, but it may not be the best. My solution goes like this:
In the control's constructor, for each property you want to set, you:
Store the desired value in a private variable.
Assign the variable's value to the property.
Assign an event handler that assigns the variable's value to the property whenever the property is changed.
A downside is the amount of coding for each property. Also, you wouldn't be able to change the properties from the Properties pane.
Do you think about something like:
if (this.DesignMode)
{
// do somthing
}
If you put this into the constructor remember to call InitializeComponent() before.
What about:
Private Function GetPropertyByName(ByVal propName As String) _
As PropertyDescriptor
Dim prop As PropertyDescriptor
prop = TypeDescriptor.GetProperties(l_dWindow)(propName)
If prop Is Nothing Then
Throw New ArgumentException( _
"Matching ColorLabel property not found!", propName)
Else
Return prop
End If
End Function
Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
GetPropertyByName("AnyPublicProperty").SetValue(AnyControl, "AnyStringVALUE")
Me.DialogResult = DialogResult.OK
End Sub
Related
I am designing a WPF application following MVVM. My ViewModel is exposing one Double property called DoubleValue, which is binding to a TextBox in the View. I have set "ValidatesOnDataErrors=True" for the binding. So if the user types a string which can't be converted to a Double, it display the red background.
In my ViewModel I also have a Command object, let's call SaveCommand, whose CanExecute delegate is depending on whether there is any error in the VM (my ViewModelBase class implements IDataErrorInfo, I have an overridable ValidatePropertyByName function and the validation errors are stored in a dictionary.) But now my problem is, if I give an invalid string in the TextBox, since the conversion fails, it never calls the setter of the binding property value. In another word, the ValidatePropertyByName is not called and the error dictionary remains the previous state, which normally is clean. So if now the user click the Save button (which is enabled since the error dictionary is clean), the SaveCommand executes with the previous valid double value to save. This is obviously not good.
So how can I make my ViewModel aware of such conversion errors?
UPDATE:
Some code example:
The binding property is like this:
Public Property DoubleValue As Double
Get
Return _doubleValue
End Get
Set(value As Double)
If value <> _doubleValue Then
_doubleValue = value
RaisePropertyChanged("DoubleValue")
End If
End Set
End Property
Private _doubleValue As Double
My binding is like this:
<TextBox Grid.Row="3" Text="{Binding DoubleValue, ValidatesOnDataErrors=True}" />
And now my problem is: if I give a string "XXX" in the text box, since it can't be converted to a double value, the setter of DoubleValue is never get called. And so the property value remains the previous(valid) value. Now if my SaveCommand gets executed, it will do the save operation with this previous valid value, which will make the user confused.
the most easy way is to just use string properties in your viewmodel. then you get all input from the user and can validate it in your viewmodel. the drawback is that you have to convert the values to the right type when you go to the model.
if you dont want this you have to create your own controls or better behaviors so that the use can just input values that your viewmodel expect. eg. NumericInputBehavior.
You cannot simply put these two things together. One is the regular validation inside the ViewModel. The other are control-specific problems, like unconvertible values.
So there are two possible ways to solve this:
1) Don't use a converter. Just bind the string. Inside the ViewModel you can then use the validation to check for a valid value. (More MVVM)
2) Store your ValidationErrors on the controlside and merge them with the viewmodel errors. This is not easy but a good way to create one source for binding against ALL problems within your UI. We are doing this for complex textboxes at work. This means manual code in the controls but for complex customcontrols this is OK, I believe.
edit: just to elaborate a little on the 2nd point. We are having a DependencyProperty of Type ObservableCollection inside the Control. Then you can bind this Collection to a ViewModel Property and as soon as your control moves an Error inside the collection it is available inside the viewModel. You can then use this collection inside your validation implementation. This works pretty well for larger controls.
Edit2: For the MarkInvalid Stuff I mentioned in the comment. It would look like this:
DataErrorValidationRule validationRule = new DataErrorValidationRule();
ValidationError validationError = new ValidationError(validationRule, myTextBox.GetBindingExpression(TextBox.TextProperty)) { ErrorContent = "My custom message" };
Validation.MarkInvalid(myTextBox.GetBindingExpression(TextBox.TextProperty), validationError);
You would call in from inside a TextChanged when you can't convert the new given value or
Validation.ClearInvalid(myTextBox.GetBindingExpression(TextBox.TextProperty))
Maybe that will help?
Let me start by saying I am very new to WPF so be gentle.
It seems like this should be easy but I am just missing something. I have implemented INotifyPropertyChanged on a few classes/properties and started binding them to elements in XAML but now I have a little more complex binding to make then updating text or changing a color. What I need is when the Alarm property of my object is set to true I need to change colors, start an animation, create and display other elements in the control. I thought I could just call a function in my control when the property is changed but since WPF hides how an element is "bound" to the model's property I am not sure how to wire that up. Is there a better way to perform this type of more complex response to a property change? If not are there any samples out there? I have not been able to find anything close to what I am looking for but I may not be searching with the correct terms.
What I need is when the Alarm property of my object is set to true I
need to change colors, start an animation, create and display other
elements in the control.
Change colors: Bind the Color/Foreground of the element you want to change the color of, to the boolean that sets the alarm, and add an IValueConverter in the binding that returns a Brush based on the boolean.
Start an animation: Use a (data)trigger on whichever element needs to be animated, in that trigger, use a Storyboard to define the animation you want.
create and display other elements in the control: that really depends on how well you did your MVVM, if the elements are a visualisation of an ObservableCollection through a ListBox/ListView/ItemsControl (which it should), wire up a Command to whatever sets the alarm on/off (the Button class has a Command property built in, other UIElements may require the use of System.Windows.Interactivity) and in the method that this Command will point to, add a new item to the ObservableCollection, the ItemsControl will automatically reflect the change by adding a new control.
Of course this is just raw information, and you're probably not familiar will all these things, that's when Google comes into play ;)
HTH,
Bab.
For complex behaviour in response to a property changed event, you should use the following approach: (I'm typing this freestyle so pardon any minor syntax errors)
class MyClass : INotifyPropertyChanged
{
//Presumably you've already done this part
private object _myProperty = null;
public object MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public MyClass()
{
this.PropertyChanged += My_PropertyChanged;
}
private void My_PropertyChanged( object sender, PropertyChangedEventArgs e)
{
if( e.PropertyName == "MyProperty" )
{
//Do Something complicated
}
}
Your timer / alarm just needs to update the bound property when it elapses. The property should then raise the PropertyChanged event to notify the GUI to update.
You should take a look at DataTriggers that get fired when a property changes in the view model. The EnterActions and ExitActions will allow you to play a Storyboard when the value of the property changes. Here's an example of how it is used MSDN. You can use a content control and style the Template or ContentTemplate to add all of your elements and have it control the Visibility or Opacity of the other elements.
I don't think you are looking at this right. Your class has logic, does calculation, enforces constraints, and enforces relationships among properties. If you have an alarm hopefully there is some business logic to deal with this and should be done in the class (not the UI). A UI is not built to handle an alarm it is built to display status and actions of that alarm. You will make new control visible in an alarm situation and hide other. On the animation I think you might want to throw an event that you listen for to start the animation. The idea there is that when an alarm is thrown to can register additional handles - you want to separate the business logic from the UI (not pull the business logic into the UI).
I have a custom control that has two dependency properties. They are set as such:
Public Shared ReadOnly ValueBindingProperty As DependencyProperty = _
DependencyProperty.Register("ValueBinding", GetType(String), GetType(HomePageControl), New PropertyMetadata(String.Empty))
Public Property ValueBinding As String
Get
Return DirectCast(Me.GetValue(HomePageControl.ValueBindingProperty), String)
End Get
Set(value As String)
Me.SetValue(HomePageControl.ValueBindingProperty, value)
End Set
End Property
I assess them in the PropertyChangedCallback of another DP like this:
Dim hpc As HomePageControl = DirectCast(d, HomePageControl)
Dim valueBindingString as String = hpc.ValueBinding
And then I use it where I need it.
The value is there the first time that the page is loaded and the control is loaded. Once I navigate away from the page - using Silverlight navigation loading a new "view" into the frame - the value disappears and when I go back to the page/view that the control is on the value equals the default value instead of what it is set to in the xaml. What happened to the value?
I have other DP that are strings that remain set. As far as I can tell these two are set the same as the others. Why would these lose their value when the others don't?
When navigation occurs, Silverlight removes the old page from the tree and adds a new one which has just been created. Therefore, when you navigate to a URI and come back, you get a new control which is in its default state.
You should store the state of the controls somewhere aside from them. It may be an in-memory object or a server-side database table. The choice depends on your requirements. Probably, the best and a universal approach is applying the MVVM pattern.
I'm having one hell of a time trying to get my databinding to work correctly. I have reason to believe that what I'm trying to accomplish can't be done, but we'll see what answers I get.
I've got a UserControl. This UserControl contains nothing more than a button. Now within the code behind, I've got a property name IsBookmarked. When IsBookmarked is set, code is run that animates the look of the button. The idea is that you click the button and it visually changes. We'll call this UserControl a Bookmark control.
Now I have another control, which we'll call the FormControl. My FormControl contains a child Bookmark control. I've tried to do databinding on my Bookmark control, but it's not working. Here's some code to help you out.
This is the XAML and Loaded event handler of my control. As you can see it contains a child element that is a custom control (bookmark). So once this control loads, it's DataContext is set to an new instance of an Employee object. Silverlight also sets the DataContext property of my child bookmark control to the same instance. I've verified this by debugging. If my parent has a valid DataContext set then why can't my child control (bookmark) property databind to it?
<UserControl ......>
<q:Bookmark x:Name="BookMarkControl1" IsBookmarked="{Binding IsSiteBookmarked}" />
</UserControl>
public void Control_Loaded(object sender, EventArgs e)
{
DataContext = new Employee { IsSiteBookmarked = True };
}
This is my custom control below. Obviously it contains more than this, but for readability I've trimmed it down to the property I'm trying to databind to.
//this is the bookmark control. I've included this control within another control, and I'm trying to databind to properties within my parents DataContext
public partial class Bookmark : UserControl
{
bool _IsBookmarked= false;
public bool IsBookmarked
{
get {return _IsBookmarked;}
set {
_IsBookmarked= value;
SwitchMode(value);
}
}
}
UPDATE
Got some javascript errors that I should mention. Firebug reports a AG_E_PARSER_BAD_PROPERTY_VALUE exception. It doesn't seem like my databinding is even working yet.
Make your IsBookmarked property on the Bookmark control a dependency property.
I presume Control_Loaded is a part of your FormControl, in which case I'm not sure you are using DataContext properly. Best double check that.
UPDATE: Yes, you are using the DataContext properly. AG_E_PARSER_BAD_PROPERTY_VALUE indicates you need to make the IsBookmarked property a dependency property, like so:
Public Property IsBookmarked() As Boolean
Get
Return Me.GetValue(IsBookmarkedProperty)
End Get
Set(ByVal value As Boolean)
Me.SetValue(IsBookmarkedProperty, value)
End Set
End Property
Public Shared ReadOnly IsBookmarkedProperty As DependencyProperty = DependencyProperty.Register("IsBookmarked", GetType(Boolean), GetType(Bookmark), New PropertyMetadata(New PropertyChangedCallback(AddressOf OnIsBookmarkedPropertyChanged)))
Private Shared Sub OnIsBookmarkedPropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim cntrl As Bookmark = TryCast(d, Bookmark)
cntrl.SetIsBookmarked(e.NewValue)
End Sub
If you only need to store the value for later use, then you don't need to do anything in the OnIsBookmarkedPropertyChanged procedure, But I put some code there as an example anyway.
Good Luck!
I don't recall the exact order in which databinding is evaluated (and I'm too lazy to go look it up), but as I recall, it initially happens BEFORE the form's Loaded event fires, and without making the IsBookmarked property a dependency property, or at least using INotifyPropertyChanged, it may have trouble establishing the datacontext appropriately. I'd recommend either implementing INotifyPropertyChanged or making IsBookmarked a dependency property. DataBinding is tough enough to get right in the best of circumstances (see my long, bad-tempered rant about it here), and you'll just be making it more difficult on yourself if you aren't setting up your properties in the way that it expects.
The control exposes a IsSiteBookmarked property(which I believe should be a DependencyProperty) but the control is binding to a IsBookmarked which is not shown. Is this intentional? Have you checked your Visual Studio output window for binding errors?
Addition 1:
Since you have fixed the typo in your question and added that there is an error being reported.
Start by clearing up the AG_E_PARSER_BAD_PROPERTY_VALUE problem. Is there a line number and start position in the error message? Start looking there. One strategy is to start taking out XAML until there is no longer an error. This will narrow down the offending code.
Running in debug, mode check for binding errors in the output window.
You might want to also post the Employee class code, especially the IsSiteBookmarked property.
Typically when doing databinding to an object you will want to leverage the INotifyPropertyChanged interface and implement that so that the control can properly invalidate it's property value. Unless you use INotifyPropertyChanged with Mode=TwoWay then any code that changes your DataContext's IsSiteBookmarked will have no effect.
I have a "Login" button that I want to be disabled until 3 text boxes on the same WPF form are populated with text (user, password, server).
I have a backing object with a boolean property called IsLoginEnabled which returns True if and only if all 3 controls have data. However, when should I be checking this property? Should it be on the LostFocus event of each of the 3 dependent controls?
Thanks!
vg1890
I'd get the "backing object" to raise the IsLoginEnabled changed event when any of the 3 fields are updated. You can then bind the button to the IsLoginEnabled property and not have to keep checking it.
The pseudocode would look something like this:
Public Event IsLoginEnabledChanged As EventHandler
Public Property User() As String
Get.. ' snipped for brevity
Set(ByVal value As String)
mUser = value
RaiseEvent IsLoginEnabledChanged(Me, New EventArgs())
End Set
' do the same in the Set for Password() and Server() properties
The trick to this is naming the Event [PropertyName]Changed (i.e. IsLogonEnabledChanged) - because raising this event will automagically notify any bound controls :o)
Yes, I would say the easiest option would be to check it on the LostFocus or TextChanged event of each of those controls. However, if you want to do a little heavier lifting, you could implement the boolean as a dependency property that you could have bound to the button's Enable.
http://msdn.microsoft.com/en-us/library/ms750428.aspx
Can you just databind your IsLoginEnabled right to the Enabled property of the login button?
I think you could use RoutedCommands one of the most useful features of WPF. Basically add a CommandBinding, to use OnExecute and OnQueryCommandEnabled to manage button's enabled state.
Check out this wonderful explanation on using RoutedCommands in WPF