I want to create a Silverlight 2 control that has two content areas. A Title and a MainContent. So the control would be:
<StackPanel>
<TextBlock Text=" CONTENT1 "/>
<Content with CONTENT2 "/>
</StackPanel>
When I use the control I should just be able to use:
<MyControl Text="somecontent">main content </MyControl>
How can I create such a control?
You can do that easily with the ContentProperty attribute.
Then you can define your code behind as:
[ContentProperty("Child")]
public partial class MyControl: UserControl
{
public static readonly DependencyProperty ChildProperty = DependencyProperty.Register("Child", typeof(UIElement), typeof(MyControl), null);
public UIElement Child
{
get { return (UIElement)this.GetValue(ChildProperty); }
set
{
this.SetValue(ChildProperty, value);
this.content.Content = value;
}
}
What that will do is any default content within your tags (<MyControl Text="somecontent">main content </MyControl>) - will be set as the Child property on your class. Then once it's been set you can assign it to any control you like.
Edit:
You can have as many contents as you like, but you can only have 1 auto-content (which is designated via the ContentProperty attribute).
If you want two you could do:
<MyControl>
<MyControl.Content1>Hello World</MyControl.Content1>
<MyControl.Content2>Goodbye World</MyControl.Content2>
</MyControl>
All you have to do is make sure you have the matching dependency properties in your code. Then when the property is set, just assign it to a parent content control in your XAML.
What you wanted is a Silverlight version of the WPF HeaderedContentControl
You can find a try here. http://leeontech.wordpress.com/2008/03/11/headeredcontentcontrol-sample/
Related
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"/>
I have a custom control. There is a Stack Panel with Button and TextBlock in generic.xaml:
<StackPanel>
<TextBlock x:Name="StatusText" />
</StackPanel>
Then I have
public class MyClass : Control
{
// Constructor etc.
public static readonly DependencyProperty StatusTextProperty = DependencyProperty.Register("StatusText", typeof(TextBlock), typeof(MyClass), null);
public TextBlock StatusText
{
get { return (TextBlock)this.GetValue(StatusTextProperty); }
set { SetValue(StatusTextProperty, value); }
}
}
There is if with some logic in that happens after the button is clicked.
How do I change the Text property of TextBloc?
I thought that I can do something like this
StatusText.SetValue(TextBlock.TextProperty, "Some text here.");
But it always returns NullReferenceException (Object reference not set to an instance of an object.)
Should I use PropertyChangedCallback() on dependency property or what else do I need? I am missing something ;-)
You're taking the wrong approach - instead of trying to push the text into the text block from the control's class, you need the text block to pull the value from the control's class. The main steps you need to do are:
Change the type of the dependency property from TextBlock to string.
Bind the Text property of the TextBlock in your control template to the dependency property using a TemplateBinding binding expression. Something along the lines of:
<TextBlock Text="{TemplateBinding StatusText}" />
You can then simply set the text to be displayed to the property on your control.
Hope this helps...
Chris
You can type your question on google and find answer few times faster.
I'm working on a WPF project, and my intention is to make two specific RadioButtons alter properties of another specified Component. But for now, i'm just trying to store a String inside the RadioButton.
For that, I've created a behavior class:
public class AdjustBehavior : Behavior<RadioButton>
{
With this property:
public static DependencyProperty AdjustLabelContentProperty =
DependencyProperty.RegisterAttached("LabelContent", typeof(String), typeof(AdjustBehavior),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.Inherits));
And these getters and setters:
public static String GetLabelContent(RadioButton tb)
{
return (String)tb.GetValue(AdjustLabelContentProperty);
}
public static void SetLabelContent(RadioButton tb, String value)
{
tb.SetValue(AdjustLabelContentProperty, value);
}
On the XAML side, I did this:
<RadioButton Content="Banana" Height="16" HorizontalAlignment="Left" Margin="30,216,0,0" Name="radioButton1" VerticalAlignment="Top" GroupName="a" IsThreeState="False" IsChecked="True" Checked="radioButton1_Checked" >
<int:Interaction.Behaviors>
<i:AdjustBehavior LabelContent="Apple" />
</int:Interaction.Behaviors>
</RadioButton>
Where int: is the namespace to Interaction.Behaviors and i: is the namespace to the AdjustBehavior class. But whenever I start my application, LabelContent is set to null. Why?
I didn't post the rest of my Behavior class because I think it won't matter, but I'll do if necessary.
Thanks in Advance.
Clark
You should use DependencyProperty.Register, not RegisterAttached. This isn't being used as an attached property, but rather a standard dependency property.
Attached property requires target to be attached to. In your case that target is radio button,
so you should use
<RadioButton i:AdjustBehavior.LabelContent="Apple" ... />
If you need to just create property of AdjustBehavior, use normal dependency property, not attached.
LabelContent should be either an attached property on RadioButton or dependency property on AdjustBehavior .
this is just a question to discuss - what is the best way to make a view/edit control in WPF? E.g. we have an entity object Person, that has some props (name, surname, address, phone etc.). One presentation of the control would be a read-only view. And the other would have the edit view for this same person. Example:
<UserControl x:Name="MyPersonEditor">
<Grid>
<Grid x:Name="ViewGrid" Visibility="Visible">
<TextBlock Text="Name:"/>
<TextBlock Text="{Binding Person.Name}"/>
<Button Content="Edit" Click="ButtonEditStart_Click"/>
</Grid>
<Grid x:Name="EditGrid" Visibility="Collapsed">
<TextBlock Text="Name:"/>
<TextBox Text="{Binding Person.Name}"/>
<Button Content="Save" Click="ButtonEditEnd_Click"/>
</Grid>
</Grid>
</UserControl>
I hope that the idea is clear. The two options I see right now
two grids with visibility switching and
a TabControl without its header panel
This is just a discussion question - not much trouble with it, yet I am just wondering if there are any other possibilities and elegant solutions to this.
Automatic Lock class
I wrote an "AutomaticLock" class that has an inherited attached "DoLock" property.
Setting the "DoLock" property to true re-templates all TextBoxes ComboBoxes, CheckBoxes, etc to be TextBlocks, non-editable CheckBoxes,etc. My code is set up so that other attached property can specify arbitrary template to use in locked ("view") mode, controls that should never automatically lock, etc.
Thus the same view can easily be used for both editing and viewing. Setting a single property changes it back and forth, and it is completely customizable because any control in the view can trigger on the "DoLock" property to change its appearance or behavior in arbitrary ways.
Implementation code
Here is the code:
public class AutomaticLock : DependencyObject
{
Control _target;
ControlTemplate _originalTemplate;
// AutomaticLock.Enabled: Set true on individual controls to enable locking functionality on that control
public static bool GetEnabled(DependencyObject obj) { return (bool)obj.GetValue(EnabledProperty); }
public static void SetEnabled(DependencyObject obj, bool value) { obj.SetValue(EnabledProperty, value); }
public static readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(AutomaticLock), new FrameworkPropertyMetadata
{
PropertyChangedCallback = OnLockingStateChanged,
});
// AutomaticLock.LockTemplate: Set to a custom ControlTemplate to be used when control is locked
public static ControlTemplate GetLockTemplate(DependencyObject obj) { return (ControlTemplate)obj.GetValue(LockTemplateProperty); }
public static void SetLockTemplate(DependencyObject obj, ControlTemplate value) { obj.SetValue(LockTemplateProperty, value); }
public static readonly DependencyProperty LockTemplateProperty = DependencyProperty.RegisterAttached("LockTemplate", typeof(ControlTemplate), typeof(AutomaticLock), new FrameworkPropertyMetadata
{
PropertyChangedCallback = OnLockingStateChanged,
});
// AutomaticLock.DoLock: Set on container to cause all children with AutomaticLock.Enabled to lock
public static bool GetDoLock(DependencyObject obj) { return (bool)obj.GetValue(DoLockProperty); }
public static void SetDoLock(DependencyObject obj, bool value) { obj.SetValue(DoLockProperty, value); }
public static readonly DependencyProperty DoLockProperty = DependencyProperty.RegisterAttached("DoLock", typeof(bool), typeof(ControlTemplate), new FrameworkPropertyMetadata
{
Inherits = true,
PropertyChangedCallback = OnLockingStateChanged,
});
// CurrentLock: Used internally to maintain lock state
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public static AutomaticLock GetCurrentLock(DependencyObject obj) { return (AutomaticLock)obj.GetValue(CurrentLockProperty); }
public static void SetCurrentLock(DependencyObject obj, AutomaticLock value) { obj.SetValue(CurrentLockProperty, value); }
public static readonly DependencyProperty CurrentLockProperty = DependencyProperty.RegisterAttached("CurrentLock", typeof(AutomaticLock), typeof(AutomaticLock));
static void OnLockingStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
AutomaticLock current = GetCurrentLock(obj);
bool shouldLock = GetDoLock(obj) && (GetEnabled(obj) || GetLockTemplate(obj)!=null);
if(shouldLock && current==null)
{
if(!(obj is Control)) throw new InvalidOperationException("AutomaticLock can only be used on objects derived from Control");
new AutomaticLock((Control)obj).Attach();
}
else if(!shouldLock && current!=null)
current.Detach();
}
AutomaticLock(Control target)
{
_target = target;
}
void Attach()
{
_originalTemplate = _target.Template;
_target.Template = GetLockTemplate(_target) ?? SelectDefaultLockTemplate();
SetCurrentLock(_target, this);
}
void Detach()
{
_target.Template = _originalTemplate;
_originalTemplate = null;
SetCurrentLock(_target, null);
}
ControlTemplate SelectDefaultLockTemplate()
{
for(Type type = _target.GetType(); type!=typeof(object); type = type.BaseType)
{
ControlTemplate result =
_target.TryFindResource(new ComponentResourceKey(type, "AutomaticLockTemplate")) as ControlTemplate ??
_target.TryFindResource(new ComponentResourceKey(typeof(AutomaticLock), type.Name)) as ControlTemplate;
if(result!=null) return result;
}
return null;
}
}
This code will allow you to specify an automatic lock template on a control-by-control basis or it will allow you to use default templates defined either in the assembly containing the AutomaticLock class, in the assembly containing your custom control that the lock template applies to, in your local resources in your visual tree (including your application resources)
How to define AutomaticLock templates
Default templates for WPF standard controls are defined in the assembly containing the AutomaticLock class in a ResourceDictionary merged into Themes/Generic.xaml. For example, this template causes all TextBoxes to turn into TextBlocks when locked:
<ControlTemplate TargetType="{x:Type TextBox}"
x:Key="{ComponentResourceKey ResourceId=TextBox, TypeInTargetAssembly={x:Type lc:AutomaticLock}}">
<TextBlock Text="{TemplateBinding Text}" />
</ControlTemplate>
Default templates for custom controls may be defined in the assembly containing the custom control in a ResourceDictionary mered into its Themes/Generic.xaml. The ComponentResourceKey is different in this case, for example:
<ControlTemplate TargetType="{x:Type prefix:MyType}"
x:Key="{ComponentResourceKey ResourceId=AutomaticLockTemplate, TypeInTargetAssembly={x:Type prefix:MyType}}">
...
If an application wants to override the standard AutomaticLock template for a specific type, it can place an automatic lock template in its App.xaml, Window XAML, UserControl XAML, or in the ResourceDictionary of an individual control. In each case the ComponentResourceKey should be specified the same way as for custom controls:
x:Key="{ComponentResourceKey ResourceId=AutomaticLockTemplate, TypeInTargetAssembly={x:Type prefix:MyType}}"
Lastly, an automatic lock template can be applied to a single control by setting its AutomaticLock.LockTemplate property.
How to use AutomaticLock in your UI
To use automatic locking:
Set AutomaticLock.Enabled="True" on any controls that should be automatically locked. This can be done in a style or directly on individual controls. It enables locking on the control but does not cause the control to actually lock.
When you want to lock, set AutomaticLock.DoLock="True" on your top-level control (Window, view, UserControl, etc) whenever you want the automatic locking to actually happen. You can bind AutomaticLock.DoLock to a checkbox or menu item, or you can control it in code.
Some tips on effectively switching between view and edit modes
This AutomaticLock class is great for switching betwen view and edit modes even if they are significantly different. I have several different techniques for constructing my views to accomodate layout differences while editing. Some of them are:
Make controls invisible during edit or view mode by setting either their Template or AutomaticLockTemplate to an empty template as the case may be. For example, suppose "Age" is at the top of your layout in view mode and at the bottom in edit mode. Add a TextBox for "Age" in both places. In the top one set Template to the empty template so it doesn't show in Edit mode. In the bottom one set AutomaticLockTemplate to the empty template. Now only one will be visible at a time.
Use a ContentControl to replace borders, layout panels, buttons, etc surrounding content without affecting the content. The ContentControl's Template has the surrounding borders, panels, buttons, etc for edit mode. It also has an AutomaticLockTemplate that has the view mode version.
Use a Control to replace a rectangular section of your view. (By this I actually mean an object of class "Control", not a subclass therof.) Again, you put your edit mode version in the Template and your view mode version in the AutomaticLockTemplate.
Use a Grid with extra Auto-sized rows and columns. Use a trigger on the AutomaticLock.DoLock property to update the Row, Column, RowSpan, and ColumnSpan properties of the items within the Grid. For example you could move a panel containing an "Age" control to the top by changing its Grid.Row from 6 to 0.
Trigger on DoLock to apply a LayoutTranform or RenderTransform to your items, or to set other properties like Width and Height. This is useful if you want things to be bigger in edit mode, or if you want to make a TextBox wider and move the button beside it over against the edge.
Note that you can use option #3 (a Control object with separate templates for edit and view modes) for the entire view. This would be done if the edit and view modes were completely different. In this case AutomaticLock still gives you the convenience of being able to set the two templates manually. It would look like this:
<Control>
<Control.Template>
<ControlTemplate>
<!-- Edit mode view here -->
</ControlTemplate>
</Control.Template>
<lib:AutomaticLock.LockTemplate>
<ControlTemplate>
<!-- View mode view here -->
</ControlTemplate>
</lib:AutomaticLock.LockTemplate>
</Control>
Generally it is easier to tweak a few little positions and things between the edit and view modes, and better for your user experience because the user will have consistent layout, but if you do need a complete replacement AutomaticLock gives you that power as well.
<Grid>
<TextBlock Text="Name:"/>
<LabelText="{Binding Person.Name}" Cursor="IBeam" MouseDoubleClick="lblName_dblClick"/> <!-- set the IsEditMode to true inside this event -->
<TextBox Text="{Binding Person.Name}" Visibility="{Binding IsEditMode, Converter={StaticResource BoolToVisConverter}}"/>
<Button Content="OK" Click="btnSave_Click" Visibility="{Binding IsEditMode, Converter={StaticResource BoolToVisConverter}}"/> <!-- set the IsEditMode to false inside this event -->
</Grid>
Use a command rather, if you're familiar with.
I would create a single View with 2 different configoptions , f.e. 2 different constructors to make the the relevant field editable/readonly or visible/hidden
This way you don't write redundant XAML and you can configurate all fields over code behind or ViewModel when using MVVM
Sounds like a job for a DataTemplateSelector to me. If you would rather switch the individual controls in place I would do something similar to what Veer suggested.
I have a user control in WPF which i want the text of one of it's labels to be read from the XAML where it is used. Hence..
My User Control:
<UserControl x:Class="muc">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold">
<Label.Content>
<Binding ElementName="TestName" Path="." />
</Label.Content>
</Label>
</UserControl>
Then using it:
<mycontorls:muc TestName="This is a test" />
But it doesn't works ...
How can i read the properties ?
I tried the first two answers and what I got worked in code but not on XAML (also doesn't let you see changes in the design view when using the control).
To get a property working like any other native one, here is the full process:
(The sample adds a dependency property of type Nullable to show in the control as text or a default if null)
In the code file:
1.a Define a dependency property:
public static readonly DependencyProperty MyNumberProperty = DependencyProperty.Register("MyNumber", typeof(Nullable<int>), typeof(MyUserControl), new PropertyMetadata(null, new PropertyChangedCallback(OnMyNumberChanged)));
1.b Implement the OnMyNumberChanged Callback:
private static void OnMyNumberChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args){
// When the color changes, set the icon color PlayButton
MyUserControl muc = (MyUserControl)obj;
Nullable<int> value = (Nullable<int>)args.NewValue;
if (value != null)
{
muc.MyNumberTextBlock.Text = value.ToString();
}
else
{
muc.MyNumberTextBlock.Text = "N/A";
}
}
1.c implement the MyNumber property (not dependency) to use the dependency property for easy in code use:
public Nullable<int> MyNumber{
get
{
return (Nullable<int>)GetValue(MyNumberProperty);
}
set
{
SetValue(MyNumberProperty, value);
OnTargetPowerChanged(this, new DependencyPropertyChangedEventArgs(TargetPowerProperty, value, value)); // Old value irrelevant.
}
}
In the XAML file bind the TextBlock control's text to the property (not dependency) to get the default value of the dependency property in case it is not set by the user of the control (assuming you called your root element of the user control "RootElement"):
This code:
< TextBlock Name="MyNumberTextBlock" Text="{Binding MyNumber, ElementName=RootElement}"/>
If you give the root UserControl element a name, then you can refer to it using ElementName:
<UserControl x:Class="muc"
Name="rootElement">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold">
<Label.Content>
<Binding ElementName="rootElement" Path="TestName" />
</Label.Content>
</Label>
</UserControl>
You can also use the markup extension syntax to make it a little shorter:
<UserControl x:Class="muc"
Name="rootElement">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold"
Content="{Binding TestName, ElementName=rootElement}"/>
</UserControl>
Also remember that your control will be created before its properties are set. You will either need to implement INotifyPropertyChanged or have TestName be a dependency property so that the binding is re-evaluated after the property is set.
I've only done this with Silverlight, but i wouldnt be surprised if it works in the exact same way!
// <summary>
// Xaml exposed TextExposedInXaml property.
// </summary>
public static readonly DependencyProperty TestNameProperty = DependencyProperty.Register("TestName", typeof(string), typeof(NameOfMyUserControl), new PropertyMetadata(string.empty));
// <summary>
// Gets or sets the control's text
// </summary>
public string TextExposedInXaml
{
get
{
return (string)GetValue(TestNameProperty );
}
set
{
SetValue(TestNameProperty , value);
// set the value of the control's text here...!
}
}
{Binding ElementName=x} binds to an element with name x in the element tree, there is nothing here that deals with property TestName. If you want a property on your user control, then you have to define the property in the class corresponding to that user control (in your case it would be muc), and use {Binding RelativeSource={RelativeSource FindAncestor, ...}} to reference it on your user control (see here for details), or give it a name so you can use ElementName.