I have created a UserControl named ContactPerson. It contains a persons name, phone number etc.
This usercontrol does not have a headline (ie. a label such as "_Contact Person") because I use this usercontrol in different situations.
However in one situation, I do have such a label, which means my code look somewhat like this:
<Label Content="_Contact Person"
Target="{Binding ElementName=_contactView}" />
<View1:contactView x:Name="_contactView"
DataContext="{Binding SupplierContact}"/>
I want - to set keyboard focus to the name-textbox inside the ContactPersonUserControl but it seems to be a difficult task (it is private after alle).
I do not want to move the label inside the usercontrol, which I guess would in fact be the most simple solution. It seems to me that XAML should provide a solution to this scenario.
How to do this in a simple and elegant way?
Thx
(I have a few of these controls, so I'll need to use the same solution several times).
I am not sure whether i understand your scenario correctly, But if you want to set focus to ui control from view model, then you may need to create an attached property.
Attached Property:
public static class ControlFocusExtension
{
public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(ControlFocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
public static bool GetIsFocused(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
private static void OnIsFocusedPropertyChanged(DependencyObject pDependencyObject, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)pDependencyObject;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
}
User control XAML:
Now in your View (in XAML) you can bind this property to your ViewModel:
<TextBox local:FocusExtension.IsFocused="{Binding IsNameFocused}" />
You can change the value of IsNameFocused from your view model if needed.
Alternatively if you don't want to bind this then you can use it as -
<TextBox local:FocusExtension.IsFocused="True" />
Set focusvisualstyle property null for those you dont want to set focus like this
example <Label FocusVisualStyle="{x:Null}">
and for textbox focus <TextBox Focusable="True"/>
Related
I'm new to the development of custom controls in WPF, but I tried to develop a single one to use in a application that I'm developing. This control is an autocomplete textbox. In this control, I have a DependencyProprety that has a list of possible entries so a person can choose from while entering the text
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",typeof (IList<object>),typeof (AutoCompleteTextBox),new PropertyMetadata(null));
public IList<object> ItemsSource
{
get { return (IList<object>) GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
RaiseOnPropertyChanged("ItemsSource");
}
}
I use this control in a usercontrol and associate this control to a property in the viewmodel
<CustomControls:AutoCompleteTextBox Height="23" Width="200"
VerticalAlignment="Center" Text="{Binding Path=ArticleName, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Path=Articles,
Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
</CustomControls:AutoCompleteTextBox>
I have a viewmodel that I assign on the usercontrol load to the datacontext of the usercontrol load
protected virtual void Window_Loaded(object sender, RoutedEventArgs e)
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
this.DataContext = viewModel;
SetLabels();
}
}
This viewmodel has the property Articles with values but the ItemsSource property of the control is null when I try to search in the list after the user enter some text.
Is there any special step that I missed when I create the control so use the mvvm pattern.
I hope that the explain the problem in a understandable way. Any help/hints would be welcome.
There are two issues here:
First, you're dependency property is defining the "default" value for this property to be null. You can change that by changing the metadata to specify a new collection:
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",typeof (IList<object>),typeof (AutoCompleteTextBox),
new PropertyMetadata(new List<object>));
Secondly, when using dependency properties, the setter can't contain any logic. You should keep your property set as:
public IList<object> ItemsSource
{
get { return (IList<object>) GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
This is because the setter doesn't actually get called by the binding system - only when you use code. However, since the class is a DependencyObject and this is a DP, you don't need to raise property changed events.
I've seen a library that allows me to do this inside my XAML, which sets the visibility of the control based on whether or not the user is in a role:
s:Authorization.RequiresRole="Admin"
Using that library with my database requires a bunch of coding that I can't really do right now. Ultimately here's what I want to know...
I have received the authenticated users role from my SPROC, and its currently stored in my App.xaml.cs as a property (not necessary for the final solution, just FYI for now). I want to create a property (dependency property? attached property?) that allows me to say something very similar to what the other library has: RequiresRole="Admin", which would collapse the visibility if the user is not in the Admin role. Can anyone point me in the right direction on this?
EDIT
After building the authorization class, I get the following error:
"The property 'RequiredRole' does not exist on the type 'HyperlinkButton' in the XML Namespace clr-namespace:TSMVVM.Authorization"
I'm trying to add this xaml:
<HyperlinkButton x:Name="lnkSiteParameterDefinitions"
Style="{StaticResource LinkStyle}"
Tag="SiteParameterDefinitions"
Content="Site Parameter Definitions"
Command="{Binding NavigateCommand}"
s:Authorization.RequiredRole="Admin"
CommandParameter="{Binding Tag, ElementName=lnkSiteParameterDefinitions}"/>
When I started typing the s:Authorization.RequiredRole="Admin", intellisense picked it up. I tried setting the typeof(string) and typeof(ownerclass) to HyperlinkButton to see if that would help, but it didn't. Any thoughts?
Attached property is the way to implement it. You should define a property like this:
public class Authorization
{
#region Attached DP registration
public static string GetRequiredRole(UIElement obj)
{
return (string)obj.GetValue(RequiredRoleProperty);
}
public static void SetRequiredRole(UIElement obj, string value)
{
obj.SetValue(RequiredRoleProperty, value);
}
#endregion
// Using a DependencyProperty as the backing store for RequiredRole. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RequiredRoleProperty =
DependencyProperty.RegisterAttached("RequiredRole", typeof(string), typeof(Authorization), new PropertyMetadata(RequiredRole_Callback));
// This callback will be invoked when some control will receive a value for your 'RequiredRole' property
private static void RequiredRole_Callback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var uiElement = (UIElement) source;
RecalculateControlVisibility(uiElement);
// also this class should subscribe somehow to role changes and update all control's visibility after role being changed
}
private static void RecalculateControlVisibility(UIElement control)
{
//Authorization.UserHasRole() - is your code to check roles
if (Authentication.UserHasRole(GetRequiredRole(control)))
control.Visibility = Visibility.Visible;
else
control.Visibility = Visibility.Collapsed;
}
}
PS: Have noticed too late that you were asking about Silverlight. Though I believe it works in the same way there, but I've tried it only on WPF.
I am using Expression Blend.
Let's say I got:
Public string FirstName{get;set;}
Edit: thanks for the answers, but I'm afraid people didn't understand my question. I do know how to Bind Data in Code or in XAML.
My question is if there is a way to do all that with the Expression Blend Interface without writing it directly. Only with mouse movements.
You would actually want to put the property on a View Model, and use XAML binding, but that is another story.
As you describe your example, you would first need to implement the "FirstName" property as a Dependency Property and not a simple get/set. Here is a great code-snippet from Shawn Wildermuth to save lots of typing (there is a single typo in the snippet you need to fix - "($type$)args.NewValue;"... NewValue has the wrong case in the snippet).
You can bind in XAML to a simple get/set property, but it is a one-way/one-time binding and will not update with changes.
In code, the binding requires two things to be set.
Set the DataContext of the control (or the page) and
Set a data binding on the control.
For the example you mention you could use code like the following (assumes a TextBox control called myTextBox in the Xaml):
using System.Windows;
using System.Windows.Controls;
namespace BindingCodeTest
{
public partial class BindingCode : UserControl
{
public string FirstName
{
get { return (string)GetValue(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
// Using a DependencyProperty as the backing store for FirstName.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty FirstNameProperty =
DependencyProperty.Register("FirstName",
typeof(string),
typeof(BindingCode),
new PropertyMetadata(string.Empty,
new PropertyChangedCallback(OnFirstNameChanged)));
static void OnFirstNameChanged(object sender, DependencyPropertyChangedEventArgs args)
{
// Get reference to self
BindingCode source = (BindingCode)sender;
// Add Handling Code
string newValue = (string)args.NewValue;
}
public BindingCode()
{
InitializeComponent();
myTextBox.DataContext = this;
myTextBox.SetBinding(TextBox.TextProperty, new System.Windows.Data.Binding("FirstName"));
FirstName = "First name"; // Sample change
}
}
}
In Blend 4, on the 'Data' tab > New sample Data.. > name data source as you like, f.e. 'MySampleDataSource'. Then your 'MySampleDataSource' will have a '+' button (the same Data tab on the right) with 3 options. Choose 'Add simple property' and name it 'FirstName'. Then drag that property on your TextBox or TextBlock.
The result is like this:
<TextBlock x:Name="firstName" Text="{Binding FirstName}"/>
I'm experimenting with MVVM and I can't quite wrap my mind around it.
I have a View (windows) that has several repeated controls.
Let's say I have 4 textbox - button pairs. Their behavior should be the same, after pressing a button paired textbox says "Hello World!"
I have tried several options I could think of:
One ViewModel, 4 string properties binded to textboxes, 1 command
When I bind each button to the same command I can't tell which property needs to be set.
Passing enum to CommandParameter feels awkward.
One ViewModel and UserControl that hosts a textbox and a button repeated 4 times.
Now I need to expose all the properties like Command, CommandParameter, Text etc.. Seems like a lot of work.
Still can't say which property needs to be updated after click.
Each UserControl has a ViewModel
This solves button clicking and setting property, but now I have no clue how to extract texts from nested ViewModels to the window ViewModel.
Is there any other way? I suspect DataTemplates could be of use, but I'm not sure how.
What you describe is such an abstract and contrived idea that it doesn't warrant MVVM. You're talking about TextBoxes and Buttons, which are all 'View', and not the MVVM way of thinking. You'd almost always start with a Model.
There is no 'Model' per-se here though; your specification is literally to set the value of a TextBox on a Button click. The seemingly random list of '4' items (picked out of nowhere) and a seemingly useless TextBox mean nothing.
Putting that aside and assuming that you have a set of 4 business entities, each with a field on them that is user-editable, and an action that the user can trigger, you'd do this:
Create a ViewModel class to represent an item - eg MyItemModel
Create a ViewModel class to represent the set of items (likely it will just return a Collection of the first) - eg AllMyItemsListModel
Then for the View:
Create an ItemsControl, with ItemsSource bound to an instance of the 'collection' of the second ViewModel class
For each ItemTemplate, have a template or UserControl for each item
Within the template or UserControl, bind the TextBox's Text property to the appropriate property of the first class
Bind the Button's Command property to a property on the first class returning an ICommand - using RelayCommand for example
I don't know what you mean about 'extracting texts from nested ViewModels to the window ViewModel' - what does that mean and why would you want to do it?
Hope that helps.
Number 3. Except there would just be one UserControl with viewmodel and then the Main page that would have multiple instances of that UserControl on it. If the main window needs info from the UserControl you could pass it through events or use something like MVVM Light and it's messenger class.
There's no need to create a separate ViewModel for a reusable control that has such simple behavior. Just by adding a few DependencyProperties and an event handler to the simple UserControl you can reuse the logic and only set the properties that are actually different on each instance. For the UserControl XAML you just need to hook up the TextBox to the DependencyProperty and the Button to a Click handler.
<DockPanel>
<Button Content="Reset" Click="Button_Click"/>
<TextBox Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=Text}"/>
</DockPanel>
The code for the UserControl just needs to define the properties that can be bound externally and the handler to reset the Text.
public partial class ResetTextBox : UserControl
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(ResetTextBox),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty ResetTextProperty = DependencyProperty.Register(
"ResetText",
typeof(string),
typeof(ResetTextBox),
new UIPropertyMetadata(String.Empty));
public string ResetText
{
get { return (string)GetValue(ResetTextProperty); }
set { SetValue(ResetTextProperty, value); }
}
public ResetTextBox()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Text = ResetText;
}
}
Then to use the control you just need to bind to your ViewModel string properties and set the default text that should be applied on a reset which can either be hardcoded here, or bound to other VM properties.
<StackPanel>
<local:ResetTextBox ResetText="One" Text="{Binding Name1}"/>
<local:ResetTextBox ResetText="Two" Text="{Binding Name2}"/>
<local:ResetTextBox ResetText="Three" Text="{Binding Name3}"/>
</StackPanel>
I am learning Silverlight. In the process, I'm trying to build a custom user control. My ultimate goal is to be able to write the following statement in XAML:
<my:CustomControl>
<my:CustomControl.MainControl>
<Canvas><TextBlock Text="Hello!" /></Canvas>
</my:CustomControl.MainContent>
</my:CustomControl>
The content of the control will be wrapped in a custom border. Once again, this is just a learning exercise. To append my border, I have create the following UserControl:
<UserControl x:Class="CustomControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Grid x:Name="LayoutRoot">
<Border>
<!-- CustomControl Content -->
</Border>
</Grid>
</UserControl>
The code-behind for this file looks like the following:
public partial class CustomControl : UserControl
{
public UIElement MainContent
{
get { return (UIElement)GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
public static readonly DependencyProperty MainContentProperty =
DependencyProperty.Register("MainContent", typeof(UIElement), typeof(CustomControl),
new PropertyMetadata(null));
public CustomControl()
{
InitializeComponent();
}
}
The thing I am having a problem with is getting the MainContent to appear in my CustomControl. I am confident that I am setting it properly, but I'm not sure how to display it. I really want it to be a DependencyProperty as well so I can take advantage of data binding and animations.
How do I get the MainContent to appear in the CustomControl? Thank you
First you need to wait until the rest of the control has been parsed so you need to hook the loaded event:-
public CustomControl()
{
InitializeComponent();
Loaded += new RoutedEventHandler(CustomControl_Loaded);
}
Then in the loaded event assign your MainControl property to the Child property of the border. To do that its best if you give your Border an x:Name which for now I'll simple call "border".
void CustomControl_Loaded(object sender, RoutedEventArgs e)
{
border.Child = MainControl;
}
That'll get you going. Of course you may need to deal with the MainControl property being changed dynamically so you need add a bool isLoaded field in your control and set that in the loaded event. When true your MainControl setter should assign the incoming value to the border.Child.
Things can start to get more complicated and in fact I don't recommend this approach to creating a custom control. For a better approach see Creating a New Control by Creating a ControlTemplate