Using a custom attached property with a binding - c#

I was looking at this question, but I don't understand how to actually USE the created AttachedProperty. The problem is trying to have a binding on the source of the WebBrowser control.
The code there looks like:
public static class WebBrowserUtility
{
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(WebBrowserUtility), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string) obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
WebBrowser browser = o as WebBrowser;
if (browser != null)
{
string uri = e.NewValue as string;
browser.Source = uri != null ? new Uri(uri) : null;
}
}
}
and
<WebBrowser ns:WebBrowserUtility.BindableSource="{Binding WebAddress}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Width="300"
Height="200" />
The WebAddress, what is that exactly? This is my understanding (which is probably wrong):
There's an AttachedProperty that can be attached to any object, and in this particular case, it is basically just attaching a property called BindableSource which is of type String.
When we have the "{Binding WebAddress}" it means that in some c# code somewhere that handles this .xaml file there's something that looks like:
public String WebAddress
{
// get and set here? not sure
}
And to take advantage of the property changed, I can called RaisedPropertyChanged and it will fire that static method up there?
Even when I look at it, it doesn't seem right, but I can't find anything online to help me.

There's an AttachedProperty that can be attached to any object, and in this particular case, it is basically just attaching a property called BindableSource which is of type String.
You might want to read the MSDN article on attached properties.
It is rather simple: Dependency properties work with dictionaries in which controls are associated with their values for a property, this makes it quite easy to add something like attached properties which can extend a control.
In the RegisterAttached method of the attached property a PropertyChangedCallback is hooked up which will be executed if the value changes. Using a dependency property enables binding which is the point of doing this in the first place. All the property really does is call the relevant code to navigate the browser if the value changes.
When we have the "{Binding WebAddress}" it means that in some c# code somewhere that handles this .xaml file there's something that looks like [...]
The binding references some public property or depedency property (not a field) called WebAddress inside the DataContext of the WebBrowser. For general information on data-binding see the Data Binding Overview.
So if you want to create a property which should be a binding source you either implement INotifyPropertyChanged or you create a DependencyProperty (they fire change notifications on their own and you normally do only create those on controls and UI-related classes)
Your property could look like this:
public class MyModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private string _webAddress;
public string WebAddress
{
get { return _webAddress; }
set
{
if (value != _webAddress)
{
_webAddress = value;
NotifyPropertyChanged("WebAddress");
}
}
}
}
Here you have to raise the PropertyChanged event in the setter as you suspected. How to actually declare working bindings in XAML is a rather broad topic sp i would like to direct you to the aforementioned Data Binding Overview again which should explain that.
And to take advantage of the property changed, I can called RaisedPropertyChanged and it will fire that static method up there?
The event is fired to trigger the binding to update, this in turn changes the value of the attached property which in turn causes the PropertyChangedCallback to be executed which eventually navigates the browser.

Related

How to refresh a window in C#/WPF?

I want to change a value (textBlock) according to an event. Then, I want to refresh my window, but I couldn't. I used invalidateVisual as well as solutions of other posts, but nothing worked.
Thank you in advance
Several solutions (the first and second one does not make use of databinding).
txtMyControl.text = "New value";
If not on the main thread, you could use the dispatcher to update the value.
Application.Current.Dispatcher.BeginInvoke(() => txtMyControl.text == "New Value")
However, the most WPF friendly way to do it is to use the databinding.
Any change made to the value in code will be instantly reflected in the UI.
XAML
<TextBox x:Name="txtExample" Text="{Binding MyTextProperty,Mode=TwoWay}" Height="24" Width="120"/>
In your code, you have to declare a variable that will be persistent.
private ExampleModel _ExampleModel = new ExmampleModel();
When you load your code, you associate that variable to your textbox data context.
txtExample.DataContext = _ExampleModel
Then, you have the class that will contains all the editable properties on screen (textboxes, radio boxes, etc...)
public class ExampleModel : INotifyPropertyChanged
{
private string _MyTextProperty = "test";
public string MyTextProperty {
get { return _MyTextProperty; }
set {
if (string.Compare(_MyTextProperty, value) != 0) {
_MyTextProperty = value;
RaisePropertyChanged("MyTextProperty");
}
}
}
public void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
}
Whenever you handle your event, you just have to change the value of the property containing the information and the UI will refresh accordingly. Also, since we use a two-way binding, the value from your textbox will always be the same than the one contained by MyTextProperty property in ExampleModel class, which make value retrieval very easy.
ex:
_ExampleModel.MyTextProperty = "New value";
If you were already using databinding, make sure the class used implements INotifyPropertyChanged and that the propertyChanged event is called when the property value change or otherwise it won't update the UI.
The best approach to what you're trying to do would be to use Data Binding.
You need to have a string object that will always hold the value of your textblock. Next you need to bind that object to your textblock and then use the event provided by the INotifyPropertyChanged interface and each time the value changes its representation (the textblock) will change to, no need to refresh the window.
More information here
If your event updates the textblock and the textblock you are using is bound to a string property and that property issues a NotifyPropertyChanged() in it's set method, that will cause the display to refresh as you desire.
There are other ways, but this is the easiest given my understanding of your question.
(this is similar to the other answer, but I tried to word so it is easier to understand/implement.)

Binding properties not working

Well the title may not help to much, but here is my problem. I have problems binding properties between controls in SILVERLIGHT. Here is the structure:
There is a control called "ProjectItemList", this control will recieve a string and display it in a very simple way.
There is a control called "ProjectList", this control will recive an array of string and use the control "ProjectItemList" to represent each of the strings in the array.
There is a window called "ProjectWindow", this window contains a "ProjectList" control, and a dependency property called "Nombres".
The context: The "ProjectWindow" window include a "ProjectList" control, there is a binding between ProjectWindow's dependency property "Nombres" and a dependency property "ListOfNames", here is the code of the "ListOfNames" dependency property:
public static DependencyProperty ListOfNamesProperty =
DependencyProperty.Register("ListOfNames", typeof(string[]), typeof(ProjectList), new PropertyMetadata(null));
public string[] ListOfNames
{
get
{
return (string[])GetValue(ListOfNamesProperty);
}
set
{
SetValue(ListOfNamesProperty, value);
List<ProjectItemList> auxList = new List<ProjectItemList>();
foreach (string s in value)
{
ProjectItemList il = new ProjectItemList();
il.Nombre = s;
this.lb_projects.Items.Add(il);
}
}
}
The problem is that, although the list control in "ProjectList" represent the "Nombres" property, the "Set" of "ListOfNames" is never called, so I can't create each of the "ProjectItemList" objects and represent the data.
I'm fairly new into XAML and Silverlight, so i may not considering something simple.
Thanks!
When you use dependency properties, the set and get are not called through ui binding, this is your problem.
WPF ui controls tald directrly with the dependeny property without going through your getter and setter.
You need to specify a callback to monitor property changes:
public static DependencyProperty ListOfNamesProperty =
DependencyProperty.Register("ListOfNames", typeof(string[]), typeof(ProjectList),
new PropertyMetadata(ListOfNamesChaned));
private static void ListOfNamesChaned(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
List<ProjectItemList> auxList = new List<ProjectItemList>();
foreach (string s in value)
{
ProjectItemList il = new ProjectItemList();
il.Nombre = s;
this.lb_projects.Items.Add(il);
}
}
public string[] ListOfNames
{
get
{
return (string[])GetValue(ListOfNamesProperty);
}
set
{
SetValue(ListOfNamesProperty, value);
}
}
See Chen Kinnrot's answer to know why.
For this to achieve, Raise Property Change event can be used i.e with the propertychanged event handler,Code snippet is like:
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
use OnPropertyChanged to bind the property and if any change in that property.it will notify and your binding properties automatically will be changed and ui will be updated which are bound to these properties.

Silverlight CustomControl dependency property can't be bound to parent viewmodel

I'm having a custom Control that has a dependency property
public static readonly DependencyProperty SelectedUserCodeProperty = DependencyProperty.Register(
"SelectedUserCode",
typeof(decimal),
typeof(SystemUsersControl),
new PropertyMetadata(SelectedUserCodeChanged));
public decimal SelectedUserCode
{
get
{
return (decimal)this.GetValue(SelectedUserCodeProperty);
}
set
{
this.SetValue(SelectedUserCodeProperty, value);
RaisePropertyChanged("SelectedUserCode");
}
}
This control is inside another usercontrol that I'm attempting to get the dependency property above in its viewmodel
this xaml is inside the parent control
<SystemUsers:SystemUsersControl Name="ctrlSystemUsersControl" SelectedUserCode="{Binding SelectedSystemUserCode, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,2,0,0"/>
but nothing is bound to the parent control viewmodel
I don't know what's the problem, it's my first time dealing with dependency properties, I'm considering making the two controls in one :( unless I got any help :)
Don't worry,
SelectedSystemUserCode must be a property . If its a property you will see initial value ,but what will fully support binding for your class is ,implementation of INotifyPropertyChanged. This basic interface will be a messenger for us.
1)When you implement INotifyPropertyChanged,the below event will be added to your class.
public event PropertyChangedEventHandler PropertyChanged;
2)Then create a firing method
public void FirePropertyChanged(string prop)
{
if(PropertyChanged!=null)
{
PropertyChanged(prop);
}
}
3) Register this event for not getting null reference.
in constructor this.PropertyChanged(s,a)=>{ //may do nothing };
4) //You may use Lazy < T > instead of this.
public decimal SelectedSystemUserCode
{
get{
if(_selectedSystemUserCode==null)
{
_selectedSystemUserCode=default(decimal);
}
return _selectedSystemUserCode;
}
set
{
_selectedSystemUserCode=value;
FirePropertyChanged("SelectedSystemUserCode");
//This will be messanger for our binding
}
}
In addition,
As I remember is the default value so you may give a decimal value for that,SelectedUserCodeChanged is callback method its ok also.
//new PropertyMetadata(SelectedUserCodeChanged)
new PropertyMetadata(0) or null
Hope helps.

DependencyProperty binding mode twoway but propertychangedeventhandler is null

I'm trying to follow the MVVM design paradigm with C# and XAML. I'm running into trouble with a nested user control. I'm trying to bind an element on the nested user control to one of the values in the ViewModel (which is bound to the View via the DataContext property). The same ViewModel is used for both the outer and nested user controls.
It partially works as is, but changes only go one-way from the ViewModel to the nested user control. I need the changes made in the nested user control to propagate back to the ViewModel.
Starting with the XAML for the main View, I have:
<UserControl>
<!-- ... -->
<UserControl.DataContext>
<local:MyViewModel x:Name="myViewModel" />
</UserControl.DataContext>
<!-- ... -->
<local:NestedUserControl
x:Name="nestedUserControl"
CustomNestedValue="{Binding Path=CustomValue, ElementName=myViewModel, Mode=TwoWay}" />
</UserControl>
In the C# code for the ViewModel:
// Constructor
public MyViewModel()
{
CustomValue = true;
}
private bool _customValue;
public bool CustomValue
{
get { return _customValue; }
set
{
if (_customValue != value)
{
_customValue = value;
RaisePropertyChanged ("CustomValue");
}
}
}
And in the code behind of the NestedUserControl, I have:
public static readonly DependencyProperty CustomNestedValueProperty =
DependencyProperty.Register (
"CustomNestedValue",
typeof (bool),
typeof (NestedUserControl),
new FrameworkPropertyMetatdata
{
BindsTwoWayByDefault = true,
PropertyChangedCallback =
new PropertyChangedCallback (CustomNestedValueChangedCallback)
});
public bool CustomNestedValue
{
get { return (bool) GetValue (CustomNestedValueProperty); }
set { SetValue (CustomNestedValueProperty, value); }
}
protected static void CustomNestedValueChangedCallback (
DependencyObject Source,
DependencyPropertyChangedEventArgs e)
{
bool value = (bool) e.NewValue;
NestedUserControl control = source as NestedUserControl;
control.OnCustomValueChange (value);
}
public void OnCustomValueChange (bool value)
{
RaisePropertyChanged ("CustomNestedValue");
// Do other stuff ...
}
// This function is where the nested user control gets direct
// interactions from the user which cause the dependency
// property to change. When this event occurs, the change needs
// to be communicated back up to the view model.
private void _onPreviewMouseDown (object sender, MouseButtonEventArgs e)
{
CustomNestedValue = !CustomNestedValue;
}
[Note: Not only do I set the binding mode to TwoWay when setting the binding in XAML, but I attempted to make this the default behavior of the DependencyProperty in the code above. No luck.]
Both the code behind for the nested user control and the ViewModel code contain the below PropertyChangedEventHandler event/response, which is necessary for the INotifyPropertyChanged interface. From what I understand, this is how bindings between XAML elements and the ViewModel are kept in sync.
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
try
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
catch (Exception e)
{
// ...
}
}
When I run the code, whenever the RaisePropertyChanged function is called for the NestedUserControl, the PropertyChanged event is always null. This is only a problem for the nested usercontrol, and not the outer one. Shouldn't this event be automatically set via the binding mechanism?
I've been struggling with this for several days now to no avail. Any help would be much appreciated. Thanks!
Binding to a DependencyObject operates without using the INotifyPropertyChanged interface. In fact, if you set a breakpoint in the getter or setter of the CustomNestedValue property of the NestedUserControl, you'll find it will never hit when binding in XAML. In essence, the INotifyPropertyChanged is a way of achieving binding without descending from DependencyObject.
When the MyViewModel.CustomValue is bound to the NestedUserControl, the binding code calls (in pseudo code):
NestedUserControl.SetBinding(binding, NestedUserControl.CustomNestedValueProperty)
The INotifyPropertyChanged.PropertyChanged event is never registered and will remain null. However, this doesn't necessarily answer why the value isn't going back to the ViewModel.
Regardless, you could remove a few moving pieces and go with
public static readonly DependencyProperty CustomNestedValueProperty =
DependencyProperty.Register("CustomNestedValue",
typeof (bool),
typeof (NestedUserControl),
null);
public bool CustomNestedValue
{
get { return (bool) GetValue (CustomNestedValueProperty); }
set { SetValue (CustomNestedValueProperty, value); }
}
That's how most of my DependencyProperties are written and they do support TwoWay binding.

How do I have a method in code-behind called when a property is updated?

What I need is to be able to execute code in a code-behind for my view class when a property on my view-model is updated. My understanding is that I need to use a dependency-property.
My view-model does implement INotifyPropertyChanged.
Here is the property in my view-model:
private DisplayPosition statusPosition;
public DisplayPosition StatusPosition
{
get { return this.statusPosition; }
set
{
this.statusPosition = value;
this.OnPropertyChanged("StatusPosition");
}
}
Here is my dependency property in my view:
public DisplayPosition StatusPosition
{
get { return (DisplayPosition)GetValue(StatusPositionProperty); }
set { SetValue(StatusPositionProperty, value); }
}
public static readonly DependencyProperty StatusPositionProperty =
DependencyProperty.Register(
"StatusPosition",
typeof(DisplayPosition),
typeof(TranscriptView),
new PropertyMetadata(DisplayPosition.BottomLeft));
Here is where I set up my binding in my view class (handler for this.DataContextChanged):
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Binding myBinding = new Binding("StatusPosition");
myBinding.Source = this.DataContext;
myBinding.NotifyOnTargetUpdated = true;
this.SetBinding(TranscriptView.StatusPositionProperty, myBinding);
}
When I put a break-point on the setter for the property in my view, it never gets hit even after I watch the value change in the view-model, and the PropertyChanged event raised. Ultimately, my goal is to be able to put more code in the setter.
The hairy detail, if you're curious, is that I need to move a TextBlock around between multiple StackPanels based on this value. I can't seem to find a XAML-only way of doing that.
More often than not, these problems are simple little obvious things that I've missed. Nothing I'm trying is helping me sort this one out, though.
When I put a break-point on the setter for the property in my view, it never gets hit even after I watch the value change in the view-model, and the PropertyChanged event raised. Ultimately, my goal is to be able to put more code in the setter.
You can't do this. When you're using DependencyProperties, the setter is never called when the bound property changes. It's only purpose is to allow you to set the DP from code.
You need to, instead, add a PropertyChangedCallback to the metadata on your DP, and add the extra code there. This will get called when the DP value updates, whether via binding, code, etc.

Categories