I have a UserControl with two RadioButtons and a textbox and have a corresponding ViewModel to this View.
My Question is can this control be placed in some other Page and the properties i.e Radiobuttons must be disabled in few Views and enabled in some others
How do i expose these properties or set it from other Page
I want UserControl To be a Provider View for other pages
You can create a bool DependencyPropertiy for each view:
public static readonly DependencyProperty IsRadioButtonEnabledProperty =
DependencyProperty.Register("IsRadioButtonEnabled", typeof(bool),
typeof(MainWindow), new UIPropertyMetadata(true));
public bool IsRadioButtonEnabled
{
get { return (bool)GetValue(IsRadioButtonEnabledProperty); }
set { SetValue(IsRadioButtonEnabledProperty, value); }
}
And use it inside the UserControl like this:
<RadioButton IsEnabled="{Binding IsRadioButtonEnabled, RelativeSource={
RelativeSource FindAncestor,
AncestorType={x:Type Views:YourUserControl}}}" ... />
Then you can bind to this bool property from outside of the UserControl and set whether the controls are enabled or not:
<YourUserControl IsRadioButtonEnabled={Binding IsRadioButtonEnabled} ... />
Then in your view model:
public bool IsRadioButtonEnabled { get; set; }
Then to disable the controls:
IsRadioButtonEnabled = false;
Related
I have a view which wraps a TreeView, called MbiTreeView. I want to get the selected item from the (wrapped) tree view in the view model.
The 'parent' user control which uses this custom user control:
<UserControl [...]>
<views:MbiTreeView
Grid.Row="0"
cal:Bind.Model="{Binding TreeViewModel}"
SelectedItem="{Binding SelectedItem}">
</views:MbiTreeView>
</UserControl>
The parent user control is bound to this view model:
internal sealed class SomeViewModel : PropertyChangedBase
{
public object SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
NotifyOfPropertyChange(() => SelectedItem);
}
}
public IMbiTreeViewModel TreeViewModel { get; }
public SomeViewModel(
IMbiTreeViewModel treeViewModel)
{
TreeViewModel = treeViewModel;
}
}
The MbiTreeView user control is rather straight forward. It subscribes to the selection changed event, and defines a few templates (not relevant for this question, so left them out in the question)
<TreeView ItemsSource="{Binding Items}" SelectedItemChanged="TreeView_OnSelectedItemChanged">
iew.ItemContainerStyle>
The code behind declares the dependency property:
public partial class MbiTreeView
{
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
nameof(SelectedItem),
typeof(object),
typeof(MbiTreeView),
null);
public object SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
public MbiTreeView()
{
InitializeComponent();
}
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SelectedItem = e.NewValue;
}
}
when I start the application, I can navigate through the tree view items. When I click on a treeview node, then the OnSelectedItemChanged event fires (I get into my breakpoint there). So everything goes fine up and until setting the value in the dependency property SelectedItem.
Then I would expect that the xaml binding gets notified, and updates the view model. But that never happens.
I am getting nowhere with this, help is greatly appreciated.
The SelectedItem Binding should be TwoWay:
<views:MbiTreeView ...
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
You could declare the property like shown below to make to bind TwoWay by default.
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
nameof(SelectedItem),
typeof(object),
typeof(MbiTreeView),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
I cannot properly bind to a UserControl property placed in a Page.
I have this UserControl :
<UserControl x:Class="xxxx.NumericBox" (...)>
<TextBox Name="TextBoxValue" Text="{Binding RelativeSource {RelativeSource AncestorType=UserControl}, Path=Value, Mode=TwoWay}" (...)
With this behind code :
public partial class NumericBox : UserControl
{
public NumericBox()
{
InitializeComponent();
}
public uint? Value
{
get => (uint?)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(uint?), typeof(NumericBox), new PropertyMetadata(null));
The UserControl contains others controls witch interract with Value property (+/-) and it works fine.
But I create the DependencyProperty to also bind the value in parent page.
A exemple of code in a page where I inject the UserControl :
var binding = new Binding("Line.Quantity");
binding.Mode = BindingMode.TwoWay;
var numeric = new NumericBox();
numeric.SetBinding(ValueProperty, binding);
The binding works on startup but not update Line.Quantity when I modify the Textbox...
The Line class implements INotifyPropertyChanged and notify change on Quantity.
What is the correct way to do that ?
I have seen this question but but I have not been able to correct my code :
Binding on DependencyProperty of custom User Control not updating on change
I have UserControl, lets call it as CustomDataGrid, that contains DataGrid. Remained content doesn't matter. SelectedItem property of DataGrid must be SelectedItem property of CustomDataGrid. And I wanna be able to use Binding with this property, cause I use MVVM pattern. So I have to declare SelectedItem as DependencyProperty in CustomDataGrid. But I have no ideas haw can I make it properly...
This is how DepedencyProperty-s is declared usually:
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
"SelectedItem", typeof(Object), typeof(CustomDataGrid),
new FrameworkPropertyMetadata(default(Object), SelectedItemPropertyCallback)
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});
// Optionally
private static void SelectedItemPropertyCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// dataGrid - `DataGrid` nested in `UserControl`
((CustomDataGrid)obj).dataGrid.SelectedItem = e.NewValue;
}
// Obviously, it has no any link with nested `dataGrid`. This is the problem.
public Object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
So, how can I declare SelectedItem property correctly?
You could leverage the binding framework for wiring such properties from underlying objects to outer containers
example assuming CustomDataGrid as UserControl
class CustomDataGrid : UserControl
{
public CustomDataGrid()
{
Binding b = new Binding("SelectedItem");
b.Source = this;
b.Mode = BindingMode.TwoWay;
dataGrid.SetBinding(DataGrid.SelectedItemProperty, b);
}
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedItem. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(CustomDataGrid), new PropertyMetadata(null));
}
I have created a property called SelectedItem in CustomDataGrid and set a two way binding to SelectedItem of the actual dataGrid inside.
so this will wire up these properties and will propagate any changes to and fro.
XAML solution!
Use this DependencyProperty:
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(CustomDataGrid ), new FrameworkPropertyMetadata(null)
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});
Then make your outer CustomDataGrid UserControl XAML look like this:
<UserControl x:Class="CustomDataGrid">
<DataGrid ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type CustomDataGrid}}}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type CustomDataGrid}}}">
</DataGrid>
</UserControl>
You can then use the CustomDataGrid control the same way as the DataGrid control when binding ItemsSource and SelectedItem to your view model.
I have a UserControl which I would like to load multiple times on my MainWindow.
For this I use an ItemsControl:
<ItemsControl Grid.Row="1"
ItemsSource="{Binding FtpControlList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type my:BackUpControl}">
<my:BackUpControl Margin="5"
Width="500" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
My UserControl is bound by a ViewModel. My MainWindow also has a ViewModel.
In the MainWindowViewModel I have an OberservableCollection dependency property which houlds a list of my UserControlViewModels. In the constructor of the MainWindowViewModel I add some UserControlViewModels to the List.
public MainWindowViewModel()
{
FtpControlList = new ObservableCollection<BackUpControlViewModel>();
FtpControlList.Add(new BackUpControlViewModel("View 1"));
FtpControlList.Add(new BackUpControlViewModel("View 2"));
FtpControlList.Add(new BackUpControlViewModel("View 3"));
}
public static readonly DependencyProperty FtpControlListProperty = DependencyProperty.Register("FtpControlList", typeof(ObservableCollection<BackUpControlViewModel>), typeof(MainWindowViewModel));
public ObservableCollection<BackUpControlViewModel> FtpControlList
{
get { return (ObservableCollection<BackUpControlViewModel>)GetValue(FtpControlListProperty); }
set { SetValue(FtpControlListProperty, value); }
}
Now for some reason it loads 3 times an empty usercontrol and NOT the ones in the FtpControlList property withe the property set to 'View 1, View 2 and View 3'. How can I make sure that the UserControls from the list are loaded and not empty ones?
Part of the UserControlViewModel:
// part of the UserControl Viewmodel
public BackUpControlViewModel()
{
}
public BackUpControlViewModel(string header)
{
GroupBoxHeader = header;
}
#region Dependency Properties
public static readonly DependencyProperty GroupBoxHeaderProperty = DependencyProperty.Register("GroupBoxHeader", typeof(string), typeof(BackUpControlViewModel), new UIPropertyMetadata("empty"));
public string GroupBoxHeader
{
get { return (string)GetValue(GroupBoxHeaderProperty); }
set { SetValue(GroupBoxHeaderProperty, value); }
}
public static readonly DependencyProperty FtpUrlProperty = DependencyProperty.Register("FtpUrl", typeof(string), typeof(BackUpControlViewModel), new UIPropertyMetadata("ftpurl"));
public string FtpUrl
{
get { return (string)GetValue(FtpUrlProperty); }
set { SetValue(FtpUrlProperty, value); }
}
public static readonly DependencyProperty FtpUserProperty = DependencyProperty.Register("FtpUser", typeof(string), typeof(BackUpControlViewModel), new UIPropertyMetadata("ftpUser"));
public string FtpUser
{
get { return (string)GetValue(FtpUserProperty); }
set { SetValue(FtpUserProperty, value); }
}
#endregion
It will probably be something stupid but I can't seem to find it.
The datacontext for MainWindow and the UserControl are bound to it's Viewmodel.
EDIT: BackupControl datacontext set to BackupControlViewModel (to answer Rachel's question)
public partial class BackUpControl : UserControl
{
public BackUpControl()
{
InitializeComponent();
this.DataContext = new BackUpControlViewModel();
}
}
You are overwriting the DataContext of your UserControl by setting it in the constructor of your UserControl after calling InitializeComponent();
By default, the ItemsControl will create an ItemTemplate for each item in the collection, and set it's DataContext to the item from the ItemsSource. The end result will be three new my:BackUpControl objects, with the DataContext behind those objects bound to the BackUpControlViewModel from ItemsControl.ItemsSource
Remove the line this.DataContext = new BackUpControlViewModel(); from your UserControl's constructor, and it should work like you expect
Change :
<DataTemplate DataType="{x:Type my:BackUpControl}">
To:
<DataTemplate DataType="{x:Type my:BackUpControlViewModel}">
The issue might be that your viewmodel has dependency properties. Normally you would just make your viewmodel implement INotifyPropertyChanged and the properties would be regular (not dependency properties). Unless you have a specific requirement for them to be DPs I'd switch them over.
I'm writing a value input control can be used everywhere. The control itself has a view model which set to its DataContext as usual. But when I use the control in a parent control like:
<UserControl x:Class="X.Y.Z.ParentControl">
...
<local:ValueInput Value="{Binding Path=MyValue}" />
...
</UserControl>
I'm going to bind the MyValue property of ParentControl's DataContext to the ValueInput control, but WPF tell me it cannot find the MyValue property in ValueInputViewModel class, which is the view model of ValueInput control itself. Why WPF is looking for the value from child's DataContext?
I just want to write a control which can be used like this:
<telerik:RadNumericUpDown Value="{Binding Path=NumberValue}" />
The NumberValue property is defined in in the parent's DataContext, not in the control's. This pattern works for teleriks control but not for my control.
What should I do?
For any FrameworkElement, there can be only 1 DataContext.
If UserControl has its own DataContext, it cannot use parent's DataContext.
However you can walk up to parent and get its DataContext (each time you need to reference Parent's DataContext) using RelativeSource
Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=DataContext.NumberValue}"
For this example to work, Parent (root at any level) should be Window. If it is a UserControl,
Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}}, Path=DataContext.NumberValue}"
The code is from this link provided by fiq
My friend told me not to use DataContext as the view model in a standalone control since DataContext would be easily overridden - define a ViewModel property and bind in the XAML could solve the problem. Here's an example:
View model class:
public class MyValueInputViewModel
{
public string MyText { get; set; }
}
Code behind:
public partial class MyValueInput : UserControl
{
public MyValueInput()
{
InitializeComponent();
this.ViewModel = new MyValueInputViewModel
{
MyText = "Default Text"
};
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(MyValueInputViewModel), typeof(MyValueInput));
public MyValueInputViewModel ViewModel
{
get
{
return (MyValueInputViewModel)this.GetValue(ViewModelProperty);
}
private set
{
this.SetValue(ViewModelProperty, value);
}
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(MyValueInput), new PropertyMetadata(OnValuePropertyChanged));
private static void OnValuePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
{
var input = (MyValueInput)o;
input.ViewModel.MyText = input.Value;
}
public string Value
{
get { return (string)this.GetValue(ValueProperty); }
set { this.SetValue(ValueProperty, value); }
}
}
XAML:
<UserControl x:Class="..." x:Name="Self" ...>
<Grid>
<TextBox Text="{Binding ViewModel.MyText, ElementName=Self, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</UserControl>