WPF: Passing main form property to user control - wpf

I have set up my own user control which contains two ComboBoxes, each ComboBox item source is bound to a DependencyProperty. The issue I am having is passing a property from the form that uses my user control to the user control.
Below is my user control:
public static readonly DependencyProperty ListOneProperty = DependencyProperty.Register
("ListOne", typeof(List<int>), typeof(LinkedComboBox), new PropertyMetadata(new List<int>()));
public List<int> ListOne
{
get { return (List<int>)GetValue(ListOneProperty); }
set { SetValue(ListOneProperty, value); }
}
public static readonly DependencyProperty ListTwoProperty = DependencyProperty.Register
("ListTwo", typeof(List<string>), typeof(LinkedComboBox), new PropertyMetadata(new List<string>()));
public List<string> ListTwo
{
get { return (List<string>)GetValue(ListTwoProperty); }
set { SetValue(ListTwoProperty, value); }
}
public LinkedComboBox()
{
InitializeComponent();
FillListOne();
}
And below is my MainWindow xaml:
<control:LinkedComboBox x:Name="LinkedBox" ListTwo="{Binding MyList}"/>
And MainWindow.xaml.cs:
static List<string> _myList = new List<string>{"abc","efg"};
public List<string> MyList
{
get { return _myList; }
set { _myList = value; }
}
public MainWindow()
{
InitializeComponent();
}
What do I need to get the User Control to accept a binding from the Main Window?

Everything is fine except that you need a PropertyChangedCallback to handle your property.
Here is a simple example
public static readonly DependencyProperty ListTwoProperty = DependencyProperty.Register
("ListTwo", typeof(List<string>), typeof(LinkedComboBox), new PropertyMetadata(new List<string>(), new PropertyChangedCallback(Changed)));
private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//e.NewValue here is your MyList in MainWindow.
}

Related

WPF UserControl with a url as dependencyproperty

I'm creating a wpf usercontrol which is basically a button that's supposed to open a document. I want to create it so anyone can be able to put the url of this document in the xaml. Is this possible?
Edit: I've added a dependencyproperty to store the url, but it throws an exception whenever I try to build it. The xaml looks like this:
<controls:HelpButton WidthAndHeight="40" HelpDocUrl="somUrl"/>
and my code behind for the property looks like this:
public string HelpDocUrl
{
get { return (string)GetValue(HelpDocUrlProperty); }
set { SetValue(HelpDocUrlProperty, value); }
}
public static readonly DependencyProperty HelpDocUrlProperty = DependencyProperty.Register("HelpDocUrl", typeof(string), typeof(HelpButton), new PropertyMetadata(default(string)));
Add a dependency property of the code-behind class of your UserControl:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty UrlProperty =
DependencyProperty.Register("Url", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
public string Url
{
get { return (string)GetValue(UrlProperty); }
set { SetValue(UrlProperty, value); }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
string url = Url;
//...
}
}
...that any consumer of your control can set as usual:
<local:UserControl1 Url="http://...." />

AttachedProperty is giving me bizarre error: property was already registered by 'button' [duplicate]

I am writing two dependency properties and I keep getting the "[Property] was already registered by 'FrameworkElement'" error in the design window of VS11. Here is a snippet of my code
public static readonly DependencyProperty IsEditingNumberProperty =
DependencyProperty.Register("IsEditingNumbers", typeof(bool), typeof(FrameworkElement),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
the problem seems to be the 3rd parameter (owner parameter typeof(FrameworkElement)). If I set the 3rd parameter to the class the contains two dependency properties, the error goes away, but I cannot use the properties directly from xaml. I would have to add ownership for each dependency property before I use it.
Actually, It does render correctly, but only when I first open it. Immediately after the first render it will give me an exception. At runtime, it seems to work perfectly.
Am I doing something wrong and is there a way to get rid of this annoying error?
---- Edit -----
Here is my custom class (includes 2 of the Dependency Properties):
public partial class EditableTextBox : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty IsEditingNumberProperty =
DependencyProperty.Register("IsEditingNumber", typeof(bool), typeof(FrameworkElement),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(FrameworkElement),
new FrameworkPropertyMetadata("0", FrameworkPropertyMetadataOptions.AffectsRender)
{
CoerceValueCallback = new CoerceValueCallback((sender,value) =>
{
return expressionRestaraint.Match((string)value).Value;
})
});
#endregion
public static Regex expressionRestaraint = new Regex("[-a-zA-z0-9+*.\\(\\)\\[\\]\\{\\}]*");
public string Text
{
get { (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
tbxValue.Text = (string)GetValue(TextProperty);
}
}
public bool IsEditingNumber
{
get
{
return (bool)GetValue(IsEditingNumberProperty);
}
set
{
bool old = (bool)GetValue(IsEditingNumberProperty);
if (old != value)
{
if (!value)
stopEditing();
else
startEditing();
SetValue(IsEditingNumberProperty, value);
}
}
} . . .
Use in Main Class:
<Window x:Class="VisualMathExpression.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:edit="clr-namespace:VisualMathExpression.EditableTextBox"
xmlns:all="clr-namespace:VisualMathExpression"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<edit:EditableTextBox HorizontalAlignment="Center" VerticalAlignment="Center"
Text="af" IsEditingNumber="True" /> . . .
--- Edit ---
Wrapper fixed (problem that cause xaml property not to change when ownership belonged to the declared class)
public partial class EditableTextBox : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty IsEditingNumberProperty =
DependencyProperty.Register("IsEditingNumber", typeof(bool), typeof(EditableTextBox),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender)
{
PropertyChangedCallback = new PropertyChangedCallback((sender, arg) =>
{
EditableTextBox ed = sender as EditableTextBox;
if (!(bool)arg.NewValue)
ed.stopEditing();
else
ed.startEditing();
}),
});
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(EditableTextBox),
new FrameworkPropertyMetadata("0", FrameworkPropertyMetadataOptions.AffectsRender)
{
PropertyChangedCallback = new PropertyChangedCallback((sender,arg) =>
{
EditableTextBox ed = sender as EditableTextBox;
ed.tbxValue.Text = arg.NewValue as string;
}),
CoerceValueCallback = new CoerceValueCallback((sender,value) =>
{
return expressionRestaraint.Match((string)value).Value;
})
});
#endregion
public static Regex expressionRestaraint = new Regex("[-a-zA-z0-9+*.\\(\\)\\[\\]\\{\\}]*");
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool IsEditingNumber
{
get { return (bool)GetValue(IsEditingNumberProperty); }
set { SetValue(IsEditingNumberProperty, value); }
}
The third parameter ownerType of the DependencyProperty.Register method must be the class that declares the property.
If your class is MyClass the declaration would have to look like this:
public class MyClass : DependencyObject
{
public static readonly DependencyProperty IsEditingNumberProperty =
DependencyProperty.Register(
"IsEditingNumber", typeof(bool), typeof(MyClass), ...);
// CLR wrapper
public bool IsEditingNumber
{
get { return (bool)GetValue(IsEditingNumberProperty); }
set { SetValue(IsEditingNumberProperty, value); }
}
}

MVVM DependencyProperty doesn't update when RaisePropertyChanged

I have usercontrol, and there is a DependencyProperty defined in it.
#region ImageUri
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri",
typeof(string),
typeof(ScrollableCanvas),
new PropertyMetadata(new PropertyChangedCallback(ImageUriPropertyChangedCallback)));
private static void ImageUriPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ScrollableCanvas main = sender as ScrollableCanvas;
if (main != null)
{
main.ImageUri = (string)e.NewValue;
}
}
public string ImageUri
{
get
{
return (string)GetValue(ImageUriProperty);
}
set
{
SetValue(ImageUriProperty, value);
UpdateImage();
}
}
#endregion
In the Xaml, I bind a value to it like this
<my:ScrollableCanvas Name="scrollableCanvas1" ImageUri="{Binding Path=LayerImage}" />
when I update the LayerImage in the viewmodel, the ImageUri property does not update.
Can some help on this? Thanks.
BTW: The value is updated when I set the LayerImage in the constructor of the viewmodel.
You shouldn't include your UpdateImage call in your setter, but rather in the property changed callback.
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri",
typeof(string),
typeof(ScrollableCanvas),
new PropertyMetadata(new PropertyChangedCallback(ImageUriPropertyChangedCallback)));
private static void ImageUriPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ScrollableCanvas main = sender as ScrollableCanvas;
if (main != null)
{
// Since ImageUri has already been called at this point, you can just update your image here...
main.UpdateImage();
}
}
public string ImageUri
{
get
{
return (string)GetValue(ImageUriProperty);
}
set
{
SetValue(ImageUriProperty, value);
}
}

Logically combine dependency properties

I'm using C# 4.0 and have created a DependencyObject MyView.
In MyView, I have two DependencyProperties, PropA and PropB, both are booleans.
I want a third DependencyProperty, PropC, also a bool, and simply put, should always give me (PropA || PropB).
What is the best way to accomplish this?
I was also thinking of making PropC a readonly DependencyProperty, but have read about issues with binding to readonly dp's (WPF ReadOnly Dependency Properties using MVVM)
You can use the Dependency Property changed callback for PropA and PropB to set the value for PropC (don't use the CLR property wrapper for the Dependency Properties as they are never guaranteed to be called).
If you have these three DP's
public static readonly DependencyProperty PropAProperty =
DependencyProperty.Register("PropA",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false, PropAPropertyChanged));
public static readonly DependencyProperty PropBProperty =
DependencyProperty.Register("PropB",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false, PropBPropertyChanged));
public static readonly DependencyProperty PropCProperty =
DependencyProperty.Register("PropC",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false));
public bool PropA
{
get { return (bool)this.GetValue(PropAProperty); }
set { this.SetValue(PropAProperty, value); }
}
public bool PropB
{
get { return (bool)this.GetValue(PropBProperty); }
set { this.SetValue(PropBProperty, value); }
}
public bool PropC
{
get { return (bool)this.GetValue(PropCProperty); }
set { this.SetValue(PropCProperty, value); }
}
you can use the property changed callback like this
private static void PropAPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MyView myView = source as MyView;
myView.OnPropChanged();
}
private static void PropBPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MyView myView = source as MyView;
myView.OnPropChanged();
}
public void OnPropChanged()
{
PropC = PropA || PropB;
}
This way, you'll always update the value of PropC everytime PropA or PropB changes
Also, PropC doesn't need to be a DP, it can be a normal CLR property if you implement INotifyPropertyChanged. Then the implementation can look like this instead
public void OnPropChanged()
{
OnPropertyChanged("PropC");
}
public bool PropC
{
get
{
return PropA || PropB;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You could also bind PropC to PropA and PropB with a MultiBinding. Let me know if you want an example of this as well
The linked web page is for an unusual situation, a "push" binding. That is, a one-way-to-source binding was attempted on the read-only property, not on another property trying to bind to it. By contrast, if you want your property to be bindable to other properties using a one-way binding expression on the other property, then you can use a read-only dependency property without any issues.
Edit:
Here is an example:
<Grid>
<Grid.Resources>
<local:MyObject x:Key="myObject" PropertyA="True" PropertyB="False"/>
</Grid.Resources>
<StackPanel DataContext="{StaticResource myObject}">
<CheckBox IsChecked="{Binding PropertyA}" Content="PropertyA"/>
<CheckBox IsChecked="{Binding PropertyB}" Content="PropertyB"/>
<CheckBox IsChecked="{Binding PropertyC, Mode=OneWay}" IsEnabled="False" Content="PropertyC"/>
</StackPanel>
</Grid>
and the dependency properties, one of which is read-only:
public class MyObject : DependencyObject
{
public bool PropertyA
{
get { return (bool)GetValue(PropertyAProperty); }
set { SetValue(PropertyAProperty, value); }
}
public static readonly DependencyProperty PropertyAProperty =
DependencyProperty.Register("PropertyA", typeof(bool), typeof(MyObject), new UIPropertyMetadata(false, OnPropertyAOrBChanged));
public bool PropertyB
{
get { return (bool)GetValue(PropertyBProperty); }
set { SetValue(PropertyBProperty, value); }
}
public static readonly DependencyProperty PropertyBProperty =
DependencyProperty.Register("PropertyB", typeof(bool), typeof(MyObject), new UIPropertyMetadata(false, OnPropertyAOrBChanged));
public bool PropertyC
{
get { return (bool)GetValue(PropertyCProperty); }
set { SetValue(PropertyCPropertyKey, value); }
}
private static readonly DependencyPropertyKey PropertyCPropertyKey =
DependencyProperty.RegisterReadOnly("PropertyC", typeof(bool), typeof(MyObject), new UIPropertyMetadata());
public static readonly DependencyProperty PropertyCProperty = PropertyCPropertyKey.DependencyProperty;
private static void OnPropertyAOrBChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myObject = d as MyObject;
myObject.PropertyC = myObject.PropertyA || myObject.PropertyB;
}
}

Execute Silverlight 4 Command on Load

How do you implement a Silverlight 4 command to execute when the user control loads instead of being mapped to an explicit button click?
Or simply add a trigger in xaml for your UserControl:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<si:InvokeDataCommand Command="{Binding MyCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Create a DependencyProperty of type ICommand:-
#region public ICommand LoadedCommand
public ICommand LoadedCommand
{
get { return GetValue(LoadedCommandProperty) as ICommand; }
set { SetValue(LoadedCommandProperty, value); }
}
public static readonly DependencyProperty LoadedCommandProperty =
DependencyProperty.Register(
"LoadedCommand",
typeof(ICommand),
typeof(MainPage),
new PropertyMetadata(null));
#endregion public ICommand LoadedCommand
Also add a something to act as the command parameter:-
#region public object LoadedCommandParameter
public object LoadedCommandParameter
{
get { return GetValue(LoadedCommandParameterProperty) as object; }
set { SetValue(LoadedCommandParameterProperty, value); }
}
public static readonly DependencyProperty LoadedCommandParameterProperty =
DependencyProperty.Register(
"LoadedCommandParameter",
typeof(object),
typeof(MainPage),
new PropertyMetadata(null));
#endregion public object LoadedCommandParameter
Now set up its execution like this:-
public UserControl1()
{
InitializeComponent();
Loaded += UserControl1_Loaded;
}
void UserControl1_Loaded(object sender, RoutedEventArgs e)
{
if (LoadedCommand != null && LoadedCommand.CanExecute(LoadedCommandParameter))
{
LoadedCommand.Execute(LoadedCommandParameter);
}
}
Now if your ViewModel (has a command called StartStuff) then:-
<UserControl1 LoadedCommand="{Binding StartStuff}" .... >
Thats a load of code. The concise version without the code behind
public class LoadedBehaviour
{
public static ICommand GetLoadedCommand(DependencyObject dependencyObject)
{
return (ICommand)dependencyObject.GetValue(LoadedCommandProperty);
}
public static void SetLoadedCommand(DependencyObject dependencyObject, ICommand value)
{
dependencyObject.SetValue(LoadedCommandProperty, value);
}
public static Action GetLoadedCommandExecutor(DependencyObject dependencyObject)
{
return (Action)dependencyObject.GetValue(LoadedCommandExecutorProperty);
}
public static void SetLoadedCommandExecutor(DependencyObject dependencyObject, Action value)
{
dependencyObject.SetValue(LoadedCommandExecutorProperty, value);
}
public static readonly DependencyProperty LoadedCommandProperty = DependencyProperty.Register("LoadedCommand", typeof(ICommand), typeof(FrameworkElement), new PropertyMetadata(OnPropertyChanged));
public static readonly DependencyProperty LoadedCommandExecutorProperty = DependencyProperty.Register("LoadedCommandExecutor", typeof(Action), typeof(FrameworkElement), new PropertyMetadata(OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is FrameworkElement))
{
throw new ArgumentException("Loaded command can only be used on FrameworkElements");
var executor = GetLoadedCommandExecutor(d);
if(executor == null)
{
executor = () =>
{
var command = GetLoadedCommand(d);
command.Execute(e);
};
SetLoadedCommandExecutor(d, executor);
((FrameworkElement)d).Loaded += (obj, args) => executor();
}
}
}

Resources