Within a form I have a user control for each field being returned. The control consists of a label and a texblock within a stack panel. This control is part of a datatemplate that makes up my form which is comprised of a wrap panel which contains the user controls. My intent is when the form is rendered to evaluate the bound property returned in my model and if it null set the visibility of the control to collapsed. The intent is to only have fields rendered within the form that has data being returned. The wrap panel allows for the controls to stay inline vs allowing excess white space in the form.
My initial thought was to iterate through the List that is returned and if the property in the model is null set the visibility of the control to collapsed via a dependency property. A concern I have here is with performance as some forms have over 700 fields / properties.
I was curious to learn if anyone has done a similar approach or what approach they used to control the visibility of UIElements
Thanks in advance for any suggestions
We use Dependency Properties to determine visibility of controls. We do this in concert with our Authorization library. So in our xaml, the code looks something like this:
<ListBoxItem x:Name="About"
Content="About Us"
AuthLib:Authorization.Visibility="WebUser"
Margin="10,5,10,5" />
<ListBoxItem x:Name="Accounting"
Content="Work Order Acct"
AuthLib:Authorization.Visibility="Admin, Accounting,Finance"
Margin="10,5,10,5" />
Where WebUser is any authenticated user, and obviously Accounting/Finance/Admin roles have elevated privilages.
We've done this with dozens of calls on a page without any problem, but never hundreds. Might be worth a copy/paste to see how it goes.
In case it's worthwhile, here's the visibility property in our Auth library:
#region Visibility
public static string GetVisibility(UIElement obj)
{
return (string)obj.GetValue(VisibilityProperty);
}
public static void SetVisibility(UIElement obj, string value)
{
obj.SetValue(VisibilityProperty, value);
}
/// Using a DependencyProperty as the backing store for requiresRole. This enables animation, styling, binding, etc...
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.RegisterAttached(
"Visibility",
typeof(string),
typeof(Authorization),
new PropertyMetadata(Visibility_Callback));
// This callback will be invoked when some control will receive a value for your 'Visibility' property
private static void Visibility_Callback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var uiElement = (UIElement)source;
if (App.IsAuthenticated)
{
RecalculateControlVisibility(uiElement);
}
else
{
EventHandler eh = null;
eh = delegate
{
RecalculateControlVisibility(uiElement);
};
App.Authenticated += eh;
RecalculateControlVisibility(uiElement);
}
}
private static void RecalculateControlVisibility(UIElement control)
{
//Authorization.UserHasRole() - is your code to check roles
if (Authorization.UserHasRole(GetVisibility(control)))
{
control.Visibility = Visibility.Visible;
}
else
{
control.Visibility = Visibility.Collapsed;
}
}
#endregion
Related
I need to add some logic to user control with DependencyProperty.
My logic is supposed to change properties on controls inside my UserControl.
I want to avoid building huge "dependency tree" because I have a lot of user controls. I just want to use binding in my windows (not in nested user controls).
This is my control:
public partial class BucketElevatorControl : UserControl
{
public BucketElevatorControl()
{
InitializeComponent();
}
public bool On
{
get
{
return (bool)GetValue(OnProperty);
}
set
{
SetValue(OnProperty, value);
}
}
// Using a DependencyProperty as the backing store for IsOn. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OnProperty = DependencyProperty.Register(
"On",
typeof(bool),
typeof(BucketElevatorControl),
new PropertyMetadata(
false, PropertyChangedCallback
));
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
// I want to do something with my UserControl child controls
}
}
The question is: how can I do some logic in contol code behind and take advantage of data binding?
My logic is complicated (drawing graphics, animations etc.).
You should create CoerceValueCallbacks for the properties you want to change. Those callbacks set the new values. When this property changes, you then coerce the others, like so:
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
dependencyObject.CoerceValue(MinReadingProperty);
dependencyObject.CoerceValue(MaxReadingProperty);
}
I have no idea what you mean by "dependency tree", but if you want to alter the state of stuff in your template according to changes in your control's dependency properties, you can do that with TemplateBinding and/or triggers in your control template. Write value converters if you need to. Most of what you need to do can probably be done that way.
If you need more complicated logic, you can also override OnApplyTemplate() in your control, and call GetTemplateChild() to get named controls within the control's template. For example, you might require the template to have a TextBox somewhere in it called PART_FooText; throw an exception if you get null from GetTemplateChild("PART_FooText") as TextBox. If the TextBox is there, do anything you like to it: Handle events, set properties, etc. If you like, keep a private field TextBox _PART_FooText; to fiddle with it later on, in your property-changed callbacks, other events, or whatever.
Short story
I have a BindingExpression object, I want to update the target side of the binding just like I can do it with the source (by calling BindingExpression.UpdateSource). How can I do it?
There is the similar question for WPF which I don't know how to adapt to Silverlight: Cancel combobox selection in WPF with MVVM
Long story
Given a two-way binding that binds the SelectedItem of a combobox to a property of a view model. The combobox is used for navigation so that by selecting the user tells the view model to navigate away. The view model can have some unsaved changes which the user don't want to loose accidently. So the view model throws out a confirmation dialog asking if the user really wants to navigate away and loose their changes. In case the user says no, we want to undo the selection made to the combobox, so that it was in it's original state just like before the navigation attempt was made. How can I do it?
For WPF people
Please note, there is no UpdateTarget method in Silverlight (as far as I know), so this is why this question was brought up.
If i understand you correctly, when the property changes, you should store the previous value of the SelectedItem backing field on an atrribute on your view model, and when the user decides to cancel the confirmation dialog, you should restore the SelectedItem backing field to the previous value.
If you're using regular properties for the backing field, you can write a Set method that implements this behavior:
private object selectedItemPreviousValue;
private object selectedItemBackingField;
public object SelectedItemBackingField
{
get
{
return selectedItemBackingField;
}
set
{
selectedItemPreviousValue = selectedItemBackingField;
selectedItemBackingField = value;
}
}
If you're using dependencie properties, you have to provide a PropertyMetada with a DependencyPropertyChantged callback, something like this:
public object SelectedItemBackingField
{
get { return (object)GetValue(SelectedItemBackingFieldProperty); }
set { SetValue(SelectedItemBackingFieldProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedItemBackingField. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemBackingFieldProperty =
DependencyProperty.Register("SelectedItemBackingField", typeof(object), typeof(MapApp), new PropertyMetadata(new PropertyChangedCallback(OnSelectedItemChanged));
public static OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
MyViewModel vm = (MyViewModel)sender;
vm.selectedItemPreviousValue = args.OldValue;
}
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 have a need to create an attached property for a TextBox, that enforces a rule that content is required.
NOTE: Unfortunately I am not able to use data annotations, or SL4 validation frameworks.
The textboxes are displayed within the context of a View. The View is reused in many places. When tabbing / clicking between TextBoxes within the view I want a popup message to notify the user if they have left a 'Required' TextBox empty.
Now, I have this working via the LostFocus event:
public static readonly DependencyProperty RequiredProperty =
DependencyProperty.RegisterAttached("Required", typeof(bool), typeof(TextBoxRequiredService),
new PropertyMetadata(OnRequiredChanged));
public static bool GetRequired(DependencyObject d)
{
return (bool)d.GetValue(RequiredProperty);
}
public static void SetRequired(DependencyObject d, bool value)
{
d.SetValue(RequiredProperty, value);
}
private static void OnRequiredChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = d as TextBox;
textBox.LostFocus += (s, args) => {
if (textBox.Text.Length == 0) {
MessageBox.Show("Required Field!");
textBox.Focus();
}
};
}
But this is getting triggered, obvioulsy, on every lost focus, and there are certain situations, for example closing the view, that i don't want the validation to execute.
So, does anyone have any good suggestions (or examples) on a way to get a Required Text Box service working within a definable scope of actions? Or perhaps some clever alternatives to LostFocus that I could use?
Thanks,
Mark
I have two UserControls ("UserControlParentView" and "UserControlChildView") with MVVM pattern implemented in both controls. Parent control is a container for Child control and child control's property should be updated by data binding from Parent control in order to show/hide some check box inside Child control.
Parent Control Description
UserControlParentViewModel has property:
private bool isShowCheckbox = false;
public bool IsShowCheckbox
{
get { return isShowCheckbox; }
set { isShowCheckbox = value; NotifyPropertyChanged("IsShowCheckbox"); }
}
UserControlParentViewModel - how I set DataContext of Parent control:
public UserControlParentView()
{
InitializeComponent();
this.DataContext = new UserControlParentViewModel();
}
UserControlParentView contains toggle button (in XAML), bound to UserControlParentViewModel's property IsShowCheckbox
<ToggleButton Grid.Column="1" IsChecked="{Binding IsShowCheckbox, Mode=TwoWay}"></ToggleButton>
Also Parent control contains instance of child element (somewhere in XAML)
<local:UserControlChildView IsCheckBoxVisible="{Binding IsShowCheckbox}" ></local:UserControlChildView>
so property in child control should be updated when user togggle/untoggle button.
Child control contains Boolean property to be updated from parent control, but nothing happened! Breakpoint never fired!
Property in UserControlChildView that should be updated from Parent control (here I plan to make chechBox visible/hidden in code behind):
public bool IsCheckBoxVisible
{
get { return (bool)GetValue(IsCheckBoxVisibleProperty); }
set { SetValue(IsCheckBoxVisibleProperty, value); }
}
// Using a DependencyProperty as the backing store for IsCheckBoxVisible. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckBoxVisibleProperty =
DependencyProperty.Register("IsCheckBoxVisible", typeof(bool), typeof(TopMenuButton), new PropertyMetadata(false));
So the question is - what I'm doing wrong? Why child's property is never updated? BTW - there is no any binding error warnings in Output window...
You don't state where you put the breakpoint "never fired!". My guess is you placing a break point in the set mutator method of the IsCheckBoxVisible property.
You are operating under the assumption that the binding on that property will at some point cause the set method to be called when assigning the value. However the Silverlight binding framework actuall calls SetValue directly. It passes to the SetValue method the value of IsCheckBoxVisibleProperty and the value to be assigned.
I can't see all your code, so I can't work out everything, but a couple of questions:
In your DependencyProperty.Register call, you specify typeof(TopMenuButton), which should be the UserControlChildView - I don't know if that is your view or not?
You don't set up a callback method for property changed. To do this you would have to define the properties for the FrameworkPropertyMetadata, before registering the depencencyProperty like so:
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
metadata.PropertyChangedCallback += OnSpacePropertyChanged;
You'd then have to declare OnSpacePropertyChanged, but you can at least respond to setting the property from there.
I am pretty sure you can't bind to a dependency property on a user control in Silverlight 3. I've tried it myself 9 months ago, and attempted all sorts of things to get it to work. Eventually I read somewhere that it simply wasn't possible. I have done it in WPF, so was beating my head on it for a while, thinking it was my implementation.
So, on the surface your code looks correct but this won't help.
I thought it was slated as something to be fixed in SL4.
Are you using SL4?
Hoho!! I've got it to work!
In child control I've changed property a bit
public bool IsCheckBoxVisible
{
get { return (bool)GetValue(IsCheckBoxVisibleProperty); }
set { SetValue(IsCheckBoxVisibleProperty, value); }
}
// Using a DependencyProperty as the backing store for IsCheckBoxVisible. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckBoxVisibleProperty =
DependencyProperty.Register("IsCheckBoxVisible", typeof(bool), typeof(UserControlChildView), new PropertyMetadata(false, new PropertyChangedCallback((d, dc) =>
{
var button = d as UserControlChildView;
button.CheckBoxVisibility = ((bool)dc.NewValue) ? Visibility.Visible : Visibility.Collapsed;
})));
so now I have new event subscription (see anonymous method) and it fires when in parent control IsShowCheckbox property is changed!
CheckBoxVisibility depend.property looks like this:
public Visibility CheckBoxVisibility
{
get { return (Visibility)GetValue(CheckBoxVisibilityProperty); }
set { SetValue(CheckBoxVisibilityProperty, value); }
}
// Using a DependencyProperty as the backing store for IsCheckBoxVisible. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CheckBoxVisibilityProperty =
DependencyProperty.Register("CheckBoxVisibility", typeof(Visibility), typeof(UserControlChildView), new PropertyMetadata(Visibility.Collapsed));
Constructor of serControlChildView looks like:
public UserControlChildView()
{
InitializeComponent();
this.LayoutRoot.DataContext = this;
}
So seems like it works! Thank you for your help, folks!
Ok, it seems like everything worked fine and I was confused just by non-fired breakpoint.
For simplicity I've decided to remove IsCheckBoxVisible boolean depend.property from the Child control and to bind checkBox visibility in Child control directly to CheckBoxVisibility depend.property (type is Visibility).
Also in the Parent control now I have this:
<local:UserControlChildView CheckBoxVisibility="{Binding Path=CheckboxControlVisibility}"></local:UserControlChildView>
So in the Parent control now I have CheckboxControlVisibility property (type is Visibility)