I have the following CustomControl that I want to use as a base for a Banner:
XAML:
<UserControl x:Name="userControl"
x:Class="Nova.WinRT.Controls.BannerPanel"
...>
<Grid Background="#BF8B8B8B">
<Grid Height="400" Background="#FF050A7C" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TextBlock x:Name="_Title" Text="Title" FontSize="30" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<ContentPresenter x:Name="_Content" Grid.Row="1"/>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center">
<Button x:Name="_OK" Content="OK" HorizontalAlignment="Center" Click="OkPressed" FontSize="18" Width="100" Margin="20,0" />
<Button x:Name="_Cancel" Content="Cancel" HorizontalAlignment="Center" Click="CancelPressed" FontSize="18" Width="100" Margin="20,0" />
</StackPanel>
</Grid>
</Grid>
</UserControl>
C#:
public sealed partial class BannerPanel : UserControl
{
/// <summary>
/// Title of the Banner
/// </summary>
public string Title
{
get { return _Title.Text; }
set { _Title.Text = value; }
}
/// <summary>
/// The visibility of the OK button
/// </summary>
public Visibility OKVisibility
{
get { return _OK.Visibility; }
set { _OK.Visibility = value; }
}
/// <summary>
/// The visibility of the Cancel button
/// </summary>
public Visibility CancelVisibility
{
get { return _Cancel.Visibility; }
set { _Cancel.Visibility = value; }
}
/// <summary>
/// The inner content of the panel
/// </summary>
public FrameworkElement InnerContent
{
get { return (FrameworkElement)_Content.Content; }
set { _Content.Content = value; }
}
/// <summary>
/// Fires when the Ok button is clicked
/// </summary>
public event RoutedEventHandler OkClick;
/// <summary>
/// Fires when the Cancel button is clicked
/// </summary>
public event RoutedEventHandler CancelClick;
};
But when I use it (see the following XAML), the autogenerated code does not find the elements in the inner content:
XAML:
<Controls:BannerPanel x:Name="Banner" Title="Terms and Policy" CancelVisibility="Collapsed" OkClick="OnTermsAccepted">
<Controls:BannerPanel.InnerContent>
<ScrollViewer Width="500">
<TextBlock x:Name="TermsText" TextWrapping="Wrap" FontSize="12" />
</ScrollViewer>
</Controls:BannerPanel.InnerContent>
</Controls:BannerPanel>
C#:
public sealed partial class TermsBanner : UserControl
{
/// <summary>
/// Constructor
/// </summary>
public TermsBanner()
{
this.InitializeComponent();
// Why do I have to find TermsText Manually like this??????
TermsText = (Banner.InnerContent as ScrollViewer).Content as TextBlock;
TermsText.Text = TermsOfUse;
}
};
Why do I have to manually point the variable TermsText to the correct thing? Why can't it automatically find it with FindName() like it usually does?
You should define DependencyProperty properties instead of regular ones.
Derive from ContentControl instead of UserControl.
Put your XAML in Themes/Generic.xaml in a Style/Setter/Property="Template" Value=".... It happens automatically if you create the control using the Templated Control VS item template.
Use TemplateBinding to bind properties of inner template elements to your control properties.
FindName might not work across namescopes. I never use it.
excuse the typos. Typed on a phone.
Here's a solution:
Created templated control (also known us custom control - not to be confused with a UserControl) and modified it to derive from ContentControl instead of Control:
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The Templated Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234235
namespace App124
{
[TemplatePart(Name = "_OK", Type = typeof(Button))]
[TemplatePart(Name = "_Cancel", Type = typeof(Button))]
public sealed class BannerPanel : ContentControl
{
#region Title
/// <summary>
/// Title Dependency Property
/// </summary>
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
"Title",
typeof(string),
typeof(BannerPanel),
new PropertyMetadata(null, OnTitleChanged));
/// <summary>
/// Gets or sets the Title property. This dependency property
/// indicates ....
/// </summary>
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
/// <summary>
/// Handles changes to the Title property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnTitleChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (BannerPanel)d;
string oldTitle = (string)e.OldValue;
string newTitle = target.Title;
target.OnTitleChanged(oldTitle, newTitle);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes
/// to the Title property.
/// </summary>
/// <param name="oldTitle">The old Title value</param>
/// <param name="newTitle">The new Title value</param>
private void OnTitleChanged(
string oldTitle, string newTitle)
{
}
#endregion
#region OKVisibility
/// <summary>
/// OKVisibility Dependency Property
/// </summary>
public static readonly DependencyProperty OKVisibilityProperty =
DependencyProperty.Register(
"OKVisibility",
typeof(Visibility),
typeof(BannerPanel),
new PropertyMetadata(Visibility.Visible, OnOKVisibilityChanged));
/// <summary>
/// Gets or sets the OKVisibility property. This dependency property
/// indicates ....
/// </summary>
public Visibility OKVisibility
{
get { return (Visibility)GetValue(OKVisibilityProperty); }
set { SetValue(OKVisibilityProperty, value); }
}
/// <summary>
/// Handles changes to the OKVisibility property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnOKVisibilityChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (BannerPanel)d;
Visibility oldOKVisibility = (Visibility)e.OldValue;
Visibility newOKVisibility = target.OKVisibility;
target.OnOKVisibilityChanged(oldOKVisibility, newOKVisibility);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes
/// to the OKVisibility property.
/// </summary>
/// <param name="oldOKVisibility">The old OKVisibility value</param>
/// <param name="newOKVisibility">The new OKVisibility value</param>
private void OnOKVisibilityChanged(
Visibility oldOKVisibility, Visibility newOKVisibility)
{
}
#endregion
#region CancelVisibility
/// <summary>
/// CancelVisibility Dependency Property
/// </summary>
public static readonly DependencyProperty CancelVisibilityProperty =
DependencyProperty.Register(
"CancelVisibility",
typeof(Visibility),
typeof(BannerPanel),
new PropertyMetadata(Visibility.Visible, OnCancelVisibilityChanged));
/// <summary>
/// Gets or sets the CancelVisibility property. This dependency property
/// indicates ....
/// </summary>
public Visibility CancelVisibility
{
get { return (Visibility)GetValue(CancelVisibilityProperty); }
set { SetValue(CancelVisibilityProperty, value); }
}
/// <summary>
/// Handles changes to the CancelVisibility property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnCancelVisibilityChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (BannerPanel)d;
Visibility oldCancelVisibility = (Visibility)e.OldValue;
Visibility newCancelVisibility = target.CancelVisibility;
target.OnCancelVisibilityChanged(oldCancelVisibility, newCancelVisibility);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes
/// to the CancelVisibility property.
/// </summary>
/// <param name="oldCancelVisibility">The old CancelVisibility value</param>
/// <param name="newCancelVisibility">The new CancelVisibility value</param>
private void OnCancelVisibilityChanged(
Visibility oldCancelVisibility, Visibility newCancelVisibility)
{
}
#endregion
/// <summary>
/// Fires when the Ok button is clicked
/// </summary>
public event RoutedEventHandler OkClick;
/// <summary>
/// Fires when the Cancel button is clicked
/// </summary>
public event RoutedEventHandler CancelClick;
public BannerPanel()
{
this.DefaultStyleKey = typeof(BannerPanel);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var cancelButton = (Button)GetTemplateChild("_Cancel");
var okButton = (Button)GetTemplateChild("_OK");
cancelButton.Click += CancelClick;
okButton.Click += OkClick;
}
}
}
Updated Themes/Generic.xaml to this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App124">
<Style
TargetType="local:BannerPanel">
<Setter
Property="HorizontalContentAlignment"
Value="Left" />
<Setter
Property="VerticalContentAlignment"
Value="Top" />
<Setter
Property="Title"
Value="Title" />
<Setter
Property="OKVisibility"
Value="Visible" />
<Setter
Property="CancelVisibility"
Value="Visible" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="local:BannerPanel">
<Grid
Background="#BF8B8B8B">
<Grid
Height="400"
Background="#FF050A7C"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition
Height="50" />
<RowDefinition
Height="*" />
<RowDefinition
Height="50" />
</Grid.RowDefinitions>
<TextBlock
x:Name="_Title"
Text="{TemplateBinding Title}"
FontSize="30"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<ContentPresenter
Grid.Row="1"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<StackPanel
Orientation="Horizontal"
Grid.Row="2"
HorizontalAlignment="Center">
<Button
x:Name="_OK"
Content="OK"
Visibility="{TemplateBinding OKVisibility}"
HorizontalAlignment="Center"
FontSize="18"
Width="100"
Margin="20,0" />
<Button
x:Name="_Cancel"
Content="Cancel"
Visibility="{TemplateBinding CancelVisibility}"
HorizontalAlignment="Center"
FontSize="18"
Width="100"
Margin="20,0" />
</StackPanel>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This is how it's used in MainPage.xaml:
<Page
x:Class="App124.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App124"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<local:BannerPanel
x:Name="Banner"
Title="Terms and Policy"
CancelVisibility="Collapsed"
OkClick="OnTermsAccepted">
<ScrollViewer
Width="500">
<TextBlock
x:Name="TermsText"
Text="Terms and Conditions"
TextWrapping="Wrap"
FontSize="12" />
</ScrollViewer>
</local:BannerPanel>
</Grid>
</Page>
and MainPage.xaml.cs code behind:
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace App124
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void OnTermsAccepted(object sender, RoutedEventArgs e)
{
new MessageDialog(TermsText.Text).ShowAsync();
}
}
}
Related
I am trying to implement a list of employees working in departments. There are several departments and several employees in a department. Following is my code and I have difficulty in scrolling and wrapping contents (Employee image and Name). As for wrapping the contents, if a row does not have enough space, I want the contents (image and employee's first name) to be displayed in a new line.
So far, I have tried several options but to no avail. I'm using ItemsControl I also tried adding a StackLayout instead of the WrapLayout.
Can anyone please tell me how to fix the scrolling issue and content wrapping issue? Are there any workarounds or any other layouts I can use? Thank you.
XAML
<ListView ItemsSource="{Binding Departments}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="20,20,20,20">
<Label Text="{Binding DepartmentName}" />
<Label Text="{Binding DepartmentId}" />
<local:ItemsControl ItemsSource="{Binding Employees}">
<local:ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<local:WrapLayout>
<Image Source="{Binding ImageUrl}"
WidthRequest="60"
HeightRequest="60"/>
<Label
Text="{Binding FirstName}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineBreakMode="WordWrap"/>
</local:WrapLayout>
</Grid>
</DataTemplate>
</local:ItemsControl.ItemTemplate>
</local:ItemsControl>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Update 1:
Classes
Department.cs
public class Department
{
public int DepartmentID { get; set; }
public string DepartmentName { get; set; }
// and several other properties
public List<Employee> Employees { get; set; }
}
Employee.cs
public class Employee
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string ImageUrl{ get; set; }
// and several other properties
}
Update 2
Seems like the scrolling does not happen in only one of my testing devices. But still need a layout which is capable of wrapping controls.
Update 3
I want the data to be displayed as in the image below. But right now, with the above code, the contents in the WrapLayout are not wrapped but they are resized in order to fit into one line. I want them to be wrapped if there's no space in the first line.
When you mention WrapLayout, I am assuming you mean this one as defined here.
Also, as you are already using ItemsControl, I would recommend tweaking it to support WrapLayout instead of StackLayout. For example:
public class ItemsControl : WrapLayout
{
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
"ItemsSource", typeof(IList), typeof(ItemsControl), propertyChanging: OnItemsSourceChanged);
/// <summary>
/// Gets or sets the items source - can be any collection of elements.
/// </summary>
/// <value>The items source.</value>
public IList ItemsSource
{
get { return (IList)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(
"ItemTemplate", typeof(DataTemplate), typeof(ItemsControl));
/// <summary>
/// Gets or sets the item template used to generate the visuals for a single item.
/// </summary>
/// <value>The item template.</value>
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public ItemsControl()
{
Padding = new Thickness(0);
Margin = new Thickness(0);
}
static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
{
((ItemsControl)bindable).OnItemsSourceChangedImpl((IList)oldValue, (IList)newValue);
}
void OnItemsSourceChangedImpl(IList oldValue, IList newValue)
{
// Unsubscribe from the old collection
if (oldValue != null)
{
INotifyCollectionChanged ncc = oldValue as INotifyCollectionChanged;
if (ncc != null)
ncc.CollectionChanged -= OnCollectionChanged;
}
if (newValue == null)
{
Children.Clear();
}
else
{
FillContainer(newValue);
INotifyCollectionChanged ncc = newValue as INotifyCollectionChanged;
if (ncc != null)
ncc.CollectionChanged += OnCollectionChanged;
}
}
/// <summary>
/// This method takes our items source and generates visuals for
/// each item in the collection; it can reuse visuals which were created
/// previously and simply changes the binding context.
/// </summary>
/// <param name="newValue">New items to display</param>
/// <exception cref="T:System.ArgumentNullException"></exception>
void FillContainer(IList newValue)
{
var template = ItemTemplate;
var visuals = Children;
if (template == null)
throw new NotSupportedException("ItemTemplate must be specified!");
var newVisuals = new List<View>(Children); //using a list to avoid multiple layout refresh
Children.Clear();
for (int i = 0; i < newVisuals.Count; i++)
{
newVisuals[i].IsVisible = i < newValue.Count;
}
for (int i = 0; i < newValue.Count; i++)
{
var dataItem = newValue[i];
if (visuals.Count > i)
{
if (template != null)
{
var visualItem = visuals[i];
visualItem.BindingContext = dataItem;
}
}
else
{
if (template != null)
{
// Pull real template from selector if necessary.
var dSelector = template as DataTemplateSelector;
if (dSelector != null)
template = dSelector.SelectTemplate(dataItem, this);
var view = template.CreateContent() as View;
if (view != null)
{
view.BindingContext = dataItem;
newVisuals.Add(view);
}
}
}
}
foreach (var child in newVisuals) //wish they had a nice AddRange method here
if(child.IsVisible)
Children.Add(child);
}
/// <summary>
/// This is called when the data source collection implements
/// collection change notifications and the data has changed.
/// This is not optimized - it simply replaces all the data.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">E.</param>
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
FillContainer((IList)sender);
}
}
And then change your XAML to:
<ListView ItemsSource="{Binding Departments}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="20,20,20,20">
<Label Text="{Binding DepartmentName}" />
<Label Text="{Binding DepartmentId}" />
<local:ItemsControl ItemsSource="{Binding Employees}">
<local:ItemsControl.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Image Source="{Binding ImageUrl}"
WidthRequest="60"
HeightRequest="60"/>
<Label Text="{Binding FirstName}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
</StackLayout>
</DataTemplate>
</local:ItemsControl.ItemTemplate>
</local:ItemsControl>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
UPDATE 1
I've found that binding in setter is not working in WinRT. So I got a helper class from this CodeProject article.
So my binding will be like this.
Should be work, but not working for me.
<Setter Property="helper:SetterValueBindingHelper.PropertyBinding">
<Setter.Value>
<helper:SetterValueBindingHelper
Type="WinRTXamlToolkit.Controls.DataVisualization.Charting.LineDataPoint, WinRTXamlToolkit.Controls.DataVisualization, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
Property="Background"
Binding="{Binding Color}"/>
</Setter.Value>
</Setter>
Not working due to WinRT limitation
<Setter Property="Background" Value="{Binding Color}"/>
I am getting ArgumentException - Unable to access DependencyProperty "Background" on type "LineDataPoint". What's wrong with my code ?
[ContentProperty(Name = "Values")]
public class SetterValueBindingHelper
{
/// <summary>
/// Optional type parameter used to specify the type of an attached
/// DependencyProperty as an assembly-qualified name, full name, or
/// short name.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
Justification = "Unambiguous in XAML.")]
public string Type { get; set; }
/// <summary>
/// Property name for the normal/attached DependencyProperty on which
/// to set the Binding.
/// </summary>
public string Property { get; set; }
/// <summary>
/// Binding to set on the specified property.
/// </summary>
public Binding Binding { get; set; }
/// <summary>
/// Collection of SetterValueBindingHelper instances to apply to the
/// target element.
/// </summary>
/// <remarks>
/// Used when multiple Bindings need to be applied to the same element.
/// </remarks>
public Collection<SetterValueBindingHelper> Values
{
get
{
// Defer creating collection until needed
if (null == _values)
{
_values = new Collection<SetterValueBindingHelper>();
}
return _values;
}
}
private Collection<SetterValueBindingHelper> _values;
/// <summary>
/// Gets the value of the PropertyBinding attached DependencyProperty.
/// </summary>
/// <param name="element">Element for which to get the property.</param>
/// <returns>Value of PropertyBinding attached DependencyProperty.</returns>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
Justification = "SetBinding is only available on FrameworkElement.")]
public static SetterValueBindingHelper GetPropertyBinding(FrameworkElement element)
{
if (null == element)
{
throw new ArgumentNullException("element");
}
return (SetterValueBindingHelper)element.GetValue(PropertyBindingProperty);
}
/// <summary>
/// Sets the value of the PropertyBinding attached DependencyProperty.
/// </summary>
/// <param name="element">Element on which to set the property.</param>
/// <param name="value">Value forPropertyBinding attached DependencyProperty.</param>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
Justification = "SetBinding is only available on FrameworkElement.")]
public static void SetPropertyBinding(FrameworkElement element, SetterValueBindingHelper value)
{
if (null == element)
{
throw new ArgumentNullException("element");
}
element.SetValue(PropertyBindingProperty, value);
}
/// <summary>
/// PropertyBinding attached DependencyProperty.
/// </summary>
public static readonly DependencyProperty PropertyBindingProperty =
DependencyProperty.RegisterAttached(
"PropertyBinding",
typeof(SetterValueBindingHelper),
typeof(SetterValueBindingHelper),
new PropertyMetadata(null, OnPropertyBindingPropertyChanged));
/// <summary>
/// Change handler for the PropertyBinding attached DependencyProperty.
/// </summary>
/// <param name="d">Object on which the property was changed.</param>
/// <param name="e">Property change arguments.</param>
private static void OnPropertyBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Get/validate parameters
var element = (FrameworkElement)d;
var item = (SetterValueBindingHelper)(e.NewValue);
if ((null == item.Values) || (0 == item.Values.Count))
{
// No children; apply the relevant binding
ApplyBinding(element, item);
}
else
{
// Apply the bindings of each child
foreach (var child in item.Values)
{
if ((null != item.Property) || (null != item.Binding))
{
throw new ArgumentException(
"A SetterValueBindingHelper with Values may not have its Property or Binding set.");
}
if (0 != child.Values.Count)
{
throw new ArgumentException(
"Values of a SetterValueBindingHelper may not have Values themselves.");
}
ApplyBinding(element, child);
}
}
}
/// <summary>
/// Applies the Binding represented by the SetterValueBindingHelper.
/// </summary>
/// <param name="element">Element to apply the Binding to.</param>
/// <param name="item">SetterValueBindingHelper representing the Binding.</param>
private static void ApplyBinding(FrameworkElement element, SetterValueBindingHelper item)
{
if ((null == item.Property) || (null == item.Binding))
{
throw new ArgumentException(
"SetterValueBindingHelper's Property and Binding must both be set to non-null values.");
}
// Get the type on which to set the Binding
Type type = null;
TypeInfo typeInfo = null;
if (null == item.Type)
{
// No type specified; setting for the specified element
type = element.GetType();
typeInfo = type.GetTypeInfo();
}
else
{
// Try to get the type from the type system
type = System.Type.GetType(item.Type);
if (null == type)
{
// Search for the type in the list of assemblies
foreach (var assembly in AssembliesToSearch)
{
// Match on short or full name
typeInfo = assembly.DefinedTypes
.Where(t => (t.FullName == item.Type) || (t.Name == item.Type))
.FirstOrDefault();
if (null != typeInfo)
{
// Found; done searching
break;
}
}
if (null == typeInfo)
{
// Unable to find the requested type anywhere
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
"Unable to access type \"{0}\". Try using an assembly qualified type name.",
item.Type));
}
}
else
{
typeInfo = type.GetTypeInfo();
}
}
// Get the DependencyProperty for which to set the Binding
DependencyProperty property = null;
var field = typeInfo.GetDeclaredProperty(item.Property + "Property"); // type.GetRuntimeField(item.Property + "Property");
if (null != field)
{
property = field.GetValue(null) as DependencyProperty;
}
if (null == property)
{
// Unable to find the requsted property
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
"Unable to access DependencyProperty \"{0}\" on type \"{1}\".",
item.Property, type.Name));
}
// Set the specified Binding on the specified property
element.SetBinding(property, item.Binding);
}
/// <summary>
/// Returns a stream of assemblies to search for the provided type name.
/// </summary>
private static IEnumerable<Assembly> AssembliesToSearch
{
get
{
// Start with the System.Windows assembly (home of all core controls)
yield return typeof(Control).GetTypeInfo().Assembly;
}
}
}
I need to create n line series in a chart with help of WinRT XAML Toolkit. I want to use custom style of line color, custom data point style & custom tool tip. If I have fixed number of series I will do like this.
<charting:Chart x:Name="LineChart" Title="Line Chart" Margin="70,0">
<charting:LineSeries
Title="Population 1"
IndependentValueBinding="{Binding Name}"
DependentValueBinding="{Binding Value}"
IsSelectionEnabled="True">
<charting:LineSeries.DataPointStyle>
<Style TargetType="charting:LineDataPoint">
<Setter Property="Width" Value="17" />
<Setter Property="Height" Value="17" />
<Setter Property="Background" Value="Lime"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="charting:LineDataPoint">
<Grid>
<ToolTipService.ToolTip>
<ContentControl>
<TextBlock TextAlignment="Center">
<Run Text="{Binding SeriesName}" />
<LineBreak />
<Run Text="{Binding Value,Converter={StaticResource MyConverter},ConverterParameter=TEST}" />
</TextBlock>
</ContentControl>
</ToolTipService.ToolTip>
<Ellipse Fill="Lime" Stroke="Lime" StrokeThickness="3" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</charting:LineSeries.DataPointStyle>
</charting:LineSeries>
</charting:Chart>
Now here the tool tip content & color of line & data point will be dynamic i.e. color will be random and tool tip content will be from binding. Now to create dynamic line series I tried the code behind approach, but I am having problems while binding the tool tip content from code behind. See my approach of code behind.
<charting:Chart x:Name="LineChart" Title="Line Chart" Margin="70,0" />
protected override void OnNavigatedTo(NavigationEventArgs e)
{
LineSeries line;
GenerateColors(20); //suppose I need to create 20 line series, that is dynamic value.
for (int i = 0; i < 20; i++)
{
line = new LineSeries();
line.Title = string.Format("Line [{0}]", i.ToString());
line.IndependentValueBinding = GetBinding("Name");
line.DependentValueBinding = GetBinding("Value");
line.ItemsSource = GetItems();
line.DataPointStyle = GetDataPointStyle(i);
this.LineChart.Series.Add(line);
}
}
private List<NameValueItem> GetItems()
{
List<NameValueItem> items = new List<NameValueItem>();
items.Add(new NameValueItem { Name = "Test1", Value = _random.Next(10, 100) });
items.Add(new NameValueItem { Name = "Test2", Value = _random.Next(10, 100) });
items.Add(new NameValueItem { Name = "Test3", Value = _random.Next(10, 100) });
items.Add(new NameValueItem { Name = "Test4", Value = _random.Next(10, 100) });
items.Add(new NameValueItem { Name = "Test5", Value = _random.Next(10, 100) });
return items;
}
private Style GetDataPointStyle(int ColorIndex)
{
Style style = new Style();
style.TargetType = typeof(LineDataPoint);
style.Setters.Add(new Setter(LineDataPoint.WidthProperty, 17));
style.Setters.Add(new Setter(LineDataPoint.HeightProperty, 17));
style.Setters.Add(new Setter(LineDataPoint.BackgroundProperty, GetColorBrush(ColorIndex)));
return style;
}
I also tried with creating global style of data point in xaml and bind color, but when I do that the lines are not getting color, only points are getting color. That approach is given below.
<Page.Resources>
<converter:MyConverter x:Key="MyConverter" />
<Style TargetType="charting:LineDataPoint" x:Key="MyDataPointStyle">
<Setter Property="Width" Value="17" />
<Setter Property="Height" Value="17" />
<Setter Property="Background" Value="{Binding Color}"/>
<!-- ABOVE BINDING IS NOT WORKING, IF I PASS HARD CODED VALUE IT'S GETTING -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="charting:LineDataPoint">
<Grid>
<ToolTipService.ToolTip>
<ContentControl>
<TextBlock TextAlignment="Center">
<Run Text="{Binding SeriesName}" />
<LineBreak />
<Run Text="{Binding Value,Converter={StaticResource MyConverter},ConverterParameter=TEST}" />
</TextBlock>
</ContentControl>
</ToolTipService.ToolTip>
<Ellipse Fill="{Binding Color}" Stroke="{Binding Color}" StrokeThickness="3" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
LineSeries line;
GenerateColors(20); //suppose I need to create 20 line series, that is dynamic value.
for (int i = 0; i < 20; i++)
{
line = new LineSeries();
line.Title = string.Format("Line [{0}]", i.ToString());
line.IndependentValueBinding = GetBinding("Name");
line.DependentValueBinding = GetBinding("Value");
line.ItemsSource = GetItems();
//line.DataPointStyle = GetDataPointStyle(i);
line.DataPointStyle = this.Resources["MyDataPointStyle"] as Style;
this.LineChart.Series.Add(line);
}
}
Check if changing the binding property owner type to Control as I mentioned in the comment and if it doesn't work - look at the template of the LineDataPoint - it has this part:
<Ellipse
Stroke="{TemplateBinding BorderBrush}"
Fill="{TemplateBinding Background}" />
You can change TemplateBinding to a regular Binding that gets the brush from your data and it should work. You would probably want to do something similar with the LineSeries style to color the lines.
<Polyline
Points="{TemplateBinding Points}"
Stroke="{TemplateBinding Background}"
Style="{TemplateBinding PolylineStyle}" />
Below is the xaml and c# code for handling selected items in my gridview.
I am also using MVVM Light and everything is working, including me being able to see what's inside SelectedItems.
However I when I attempt to clear the SelectedItems, my UI doesn't seem to update/reflect the changes made to SelectedItems.
I am using WinRT XAML Toolkit (http://winrtxamltoolkit.codeplex.com/) which has the BindableSelection extension on a GridView
XAML
<controls:CustomGridView
x:Name="VideoItemGridView"
Grid.Row="2"
Margin="0,-3,0,0"
Padding="116,0,40,46"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsItemClickEnabled="True"
SelectionMode="Extended"
Extensions:GridViewExtensions.BindableSelection="{Binding SelectedVideoItems, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource ViewSource}}"
ItemTemplate="{StaticResource VideoItemTemplate}">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid ItemWidth="250" ItemHeight="160" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</controls:CustomGridView>
MyViewViewModel.cs
#region Selected Items
/// <summary>
/// Gets or sets the selected video items.
/// </summary>
public ObservableCollection<object> SelectedVideoItems
{
get { return this._selectedVideoItems; }
set
{
this._selectedVideoItems = value;
this.Set("SelectedVideoItems", ref this._selectedVideoItems, value);
}
}
private ObservableCollection<object> _selectedVideoItems = new ObservableCollection<object>();
#endregion
#region App Bar Click Commands
/// <summary>
/// Gets the ClearSelection click command.
/// </summary>
public ICommand ClearSelectionClickCommand
{
get
{
return new RelayCommand(() => this.ClearSelectionOperation());
}
}
/// <summary>
/// Selects all command operation.
/// </summary>
private void ClearSelectionOperation()
{
this.SelectedVideoItems = new ObservableCollection<object>();
}
#endregion
Try clearing your selected items in ClearSelectionOperation by calling
this.SelectedVideoItems.Clear();
instead of
this.SelectedVideoItems = new ObservableCollection<object>();
If that doesn't help check if the current version of the extension from March 7 fixes the problem.
It turns out that since I am using a data template, it is actually my data model that needed to set a flag to indicate it is selected
Here's the missing piece of the puzzle. Once I update the data model bound to the grid view item (which also includes support for row/col spanning), the UI updated as expected.
Hope this helps others.
public class CustomGridView : GridView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
try
{
base.PrepareContainerForItemOverride(element, item);
dynamic _Item = item;
element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, _Item.ColumnSpan);
element.SetValue(VariableSizedWrapGrid.RowSpanProperty, _Item.RowSpan);
element.SetValue(GridViewItem.IsSelectedProperty, _Item.IsSelected);
}
catch
{
element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, 1);
element.SetValue(VariableSizedWrapGrid.RowSpanProperty, 1);
element.SetValue(GridViewItem.IsSelectedProperty, false);
}
finally
{
base.PrepareContainerForItemOverride(element, item);
}
}
I have a StackPanel in my WinRT C# Metro app that I want to use as container for a label/value pair (TextBlock and TextBox) like this:
<StackPanel Orientation="Horizontal" Width="400">
<TextBlock Text="Label" Width="150" />
<TextBox Text="Value" />
</StackPanel>
Now what I want is to let the TextBox automatically fill the remaining horizontal space of the StackPanel. Is this possible in any way? HorizontalAlignment/HorizontalContentAlignment don't work.
I know that an alternative would be to define a Grid for that. The problem is that I have this construct several times and want to use it in a Style definition. I don't want to define the Grid's definition with rows and columns x times...
Perhaps the alternative would be to define a custom user control, but I hoped there would be an easy possibility to get the TextBox to stretch.
Unfortunately the alternative (DockPanel) isnt available for Metro. You could try a WrapGrid, but I dont know if it'll solve your problem (Ive never used it).
The only real way of doing this is using a Grid as you described:
<Grid Width="400">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Label" Width="150" />
<TextBox Text="Value" Grid.Column="1" />
</Grid>
I am not sure which element you put HorizontalAlignment to, but this is what works for me:
<StackPanel>
<TextBlock Text="Title" Style="..."/>
<TextBlock Margin="0,0,0,10" Text="Description" Style="..."/>
<TextBox HorizontalAlignment="Stretch" />
<Button Margin="0" Content="Save"/>
</StackPanel>
This stretches the TextBox to the whole width of the StackPanel.
Came across this question while looking up whether a WinRT DockPanel existed.
I ended up slightly modifying the DockPanel that comes with the Silverlight Toolkit, and it seems to work.
To use, add xmlns:toolkit="using:System.Windows.Controls" to the top, and taking the question above as the scenario, we could write:
<toolkit:DockPanel Width="400">
<TextBlock Text="Label" Width="150" toolkit:DockPanel.Dock="Left" />
<TextBox Text="Value" />
</toolkit:DockPanel>
DockPanel.cs:
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
// Modified slightly for Windows Runtime support.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace System.Windows.Controls
{
public enum Dock
{
// Summary:
// Specifies that the control should be positioned on the left of the control.
Left = 0,
//
// Summary:
// Specifies that the control should be positioned on top of the control.
Top = 1,
//
// Summary:
// Specifies that the control should be positioned on the right of the control.
Right = 2,
//
// Summary:
// Specifies that the control should be positioned at the bottom of the control.
Bottom = 3,
}
/// <summary>
/// Arranges child elements around the edges of the panel. Optionally,
/// last added child element can occupy the remaining space.
/// </summary>
/// <QualityBand>Stable</QualityBand>
public class DockPanel : Panel
{
/// <summary>
/// A value indicating whether a dependency property change handler
/// should ignore the next change notification. This is used to reset
/// the value of properties without performing any of the actions in
/// their change handlers.
/// </summary>
private static bool _ignorePropertyChange;
#region public bool LastChildFill
/// <summary>
/// Gets or sets a value indicating whether the last child element
/// added to a <see cref="T:System.Windows.Controls.DockPanel" />
/// resizes to fill the remaining space.
/// </summary>
/// <value>
/// True if the last element added resizes to fill the remaining space,
/// false to indicate the last element does not resize. The default is
/// true.
/// </value>
public bool LastChildFill
{
get { return (bool)GetValue(LastChildFillProperty); }
set { SetValue(LastChildFillProperty, value); }
}
/// <summary>
/// Identifies the
/// <see cref="P:System.Windows.Controls.DockPanel.LastChildFill" />
/// dependency property.
/// </summary>
public static readonly DependencyProperty LastChildFillProperty =
DependencyProperty.Register(
"LastChildFill",
typeof(bool),
typeof(DockPanel),
new PropertyMetadata(true, OnLastChildFillPropertyChanged));
/// <summary>
/// LastChildFillProperty property changed handler.
/// </summary>
/// <param name="d">DockPanel that changed its LastChildFill.</param>
/// <param name="e">Event arguments.</param>
private static void OnLastChildFillPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DockPanel source = d as DockPanel;
source.InvalidateArrange();
}
#endregion public bool LastChildFill
#region public attached Dock Dock
/// <summary>
/// Gets the value of the
/// <see cref="P:System.Windows.Controls.DockPanel.Dock" /> attached
/// property for the specified element.
/// </summary>
/// <param name="element">
/// The element from which the property value is read.
/// </param>
/// <returns>
/// The <see cref="P:System.Windows.Controls.DockPanel.Dock" /> property
/// value for the element.
/// </returns>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "DockPanel only has UIElement children")]
public static Dock GetDock(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (Dock)element.GetValue(DockProperty);
}
/// <summary>
/// Sets the value of the
/// <see cref="P:System.Windows.Controls.DockPanel.Dock" /> attached
/// property for the specified element to the specified dock value.
/// </summary>
/// <param name="element">
/// The element to which the attached property is assigned.
/// </param>
/// <param name="dock">
/// The dock value to assign to the specified element.
/// </param>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "DockPanel only has UIElement children")]
public static void SetDock(UIElement element, Dock dock)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(DockProperty, dock);
}
/// <summary>
/// Identifies the
/// <see cref="P:System.Windows.Controls.DockPanel.Dock" />
/// attached property.
/// </summary>
public static readonly DependencyProperty DockProperty =
DependencyProperty.RegisterAttached(
"Dock",
typeof(Dock),
typeof(DockPanel),
new PropertyMetadata(Dock.Left, OnDockPropertyChanged));
/// <summary>
/// DockProperty property changed handler.
/// </summary>
/// <param name="d">UIElement that changed its Dock.</param>
/// <param name="e">Event arguments.</param>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Almost always set from the attached property CLR setter.")]
private static void OnDockPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Ignore the change if requested
if (_ignorePropertyChange)
{
_ignorePropertyChange = false;
return;
}
UIElement element = (UIElement)d;
Dock value = (Dock)e.NewValue;
// Validate the Dock property
if ((value != Dock.Left) &&
(value != Dock.Top) &&
(value != Dock.Right) &&
(value != Dock.Bottom))
{
// Reset the property to its original state before throwing
_ignorePropertyChange = true;
element.SetValue(DockProperty, (Dock)e.OldValue);
string message = string.Format(
CultureInfo.InvariantCulture,
"InvalidValue",
value);
throw new ArgumentException(message, "value");
}
// Cause the DockPanel to update its layout when a child changes
DockPanel panel = VisualTreeHelper.GetParent(element) as DockPanel;
if (panel != null)
{
panel.InvalidateMeasure();
}
}
#endregion public attached Dock Dock
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:System.Windows.Controls.DockPanel" /> class.
/// </summary>
public DockPanel()
{
}
/// <summary>
/// Measures the child elements of a
/// <see cref="T:System.Windows.Controls.DockPanel" /> in preparation
/// for arranging them during the
/// <see cref="M:System.Windows.Controls.DockPanel.ArrangeOverride(System.Windows.Size)" />
/// pass.
/// </summary>
/// <param name="constraint">
/// The area available to the
/// <see cref="T:System.Windows.Controls.DockPanel" />.
/// </param>
/// <returns>
/// The desired size of the
/// <see cref="T:System.Windows.Controls.DockPanel" />.
/// </returns>
[SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")]
protected override Size MeasureOverride(Size constraint)
{
double usedWidth = 0.0;
double usedHeight = 0.0;
double maximumWidth = 0.0;
double maximumHeight = 0.0;
// Measure each of the Children
foreach (UIElement element in Children)
{
// Get the child's desired size
Size remainingSize = new Size(
Math.Max(0.0, constraint.Width - usedWidth),
Math.Max(0.0, constraint.Height - usedHeight));
element.Measure(remainingSize);
Size desiredSize = element.DesiredSize;
// Decrease the remaining space for the rest of the children
switch (GetDock(element))
{
case Dock.Left:
case Dock.Right:
maximumHeight = Math.Max(maximumHeight, usedHeight + desiredSize.Height);
usedWidth += desiredSize.Width;
break;
case Dock.Top:
case Dock.Bottom:
maximumWidth = Math.Max(maximumWidth, usedWidth + desiredSize.Width);
usedHeight += desiredSize.Height;
break;
}
}
maximumWidth = Math.Max(maximumWidth, usedWidth);
maximumHeight = Math.Max(maximumHeight, usedHeight);
return new Size(maximumWidth, maximumHeight);
}
/// <summary>
/// Arranges the child elements of the
/// <see cref="T:System.Windows.Controls.DockPanel" /> control.
/// </summary>
/// <param name="arrangeSize">
/// The area in the parent element that the
/// <see cref="T:System.Windows.Controls.DockPanel" /> should use to
/// arrange its child elements.
/// </param>
/// <returns>
/// The actual size of the
/// <see cref="T:System.Windows.Controls.DockPanel" /> after the child
/// elements are arranged. The actual size should always equal
/// <paramref name="arrangeSize" />.
/// </returns>
[SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")]
protected override Size ArrangeOverride(Size arrangeSize)
{
double left = 0.0;
double top = 0.0;
double right = 0.0;
double bottom = 0.0;
// Arrange each of the Children
UIElementCollection children = Children;
int dockedCount = children.Count - (LastChildFill ? 1 : 0);
int index = 0;
foreach (UIElement element in children)
{
// Determine the remaining space left to arrange the element
Rect remainingRect = new Rect(
left,
top,
Math.Max(0.0, arrangeSize.Width - left - right),
Math.Max(0.0, arrangeSize.Height - top - bottom));
// Trim the remaining Rect to the docked size of the element
// (unless the element should fill the remaining space because
// of LastChildFill)
if (index < dockedCount)
{
Size desiredSize = element.DesiredSize;
switch (GetDock(element))
{
case Dock.Left:
left += desiredSize.Width;
remainingRect.Width = desiredSize.Width;
break;
case Dock.Top:
top += desiredSize.Height;
remainingRect.Height = desiredSize.Height;
break;
case Dock.Right:
right += desiredSize.Width;
remainingRect.X = Math.Max(0.0, arrangeSize.Width - right);
remainingRect.Width = desiredSize.Width;
break;
case Dock.Bottom:
bottom += desiredSize.Height;
remainingRect.Y = Math.Max(0.0, arrangeSize.Height - bottom);
remainingRect.Height = desiredSize.Height;
break;
}
}
element.Arrange(remainingRect);
index++;
}
return arrangeSize;
}
}
}
How about using a RelativePanel.
<RelativePanel Width="320" Height="180" BorderBrush="Black" BorderThickness="1">
<TextBox RelativePanel.AlignTopWithPanel="True"
RelativePanel.AlignBottomWithPanel="True"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"
TextWrapping="Wrap"/>
</RelativePanel>
figured out that the best native control to neast the contents is the grid look at my exmple that worked fine to me:
<Grid >
<TextBox x:Name="txtCountry" Margin="0,8,41,8" />
<Button x:Name="btnGetCountry" Content="..." Margin="0,8,0,8" HorizontalAlignment="Right" Click="btnGetCountry_Click"> </Button>
</Grid>
this will generate a nice adaptable layout with a textbox steching and a butoon on it side that maintains his aspect
I have the following in my root ResourceDictionary. The Foreground = Red part works, but the custom attached dependency property does not get set.
I can set it manually through code, but I obviously want to avoid having to set it for every textbox. Does this work in Silverlight? I have seen some posts about doing it in WPF, and my code looks right (to me).
<Style TargetType="TextBox">
<Setter Property="controls:TextBoxContextMenu.HasContextMenu" Value="True" />
<Setter Property="Foreground" Value="Red" />
</Style>
/// <summary>
/// Gets the value of the HasContextMenu attached property for a specified TextBox.
/// </summary>
/// <param name="element">The TextBox from which the property value is read.</param>
/// <returns>The HasContextMenu property value for the TextBox.</returns>
public static bool GetHasContextMenu(TextBox element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool)element.GetValue(HasContextMenuProperty);
}
/// <summary>
/// Sets the value of the HasContextMenu attached property to a specified TextBox.
/// </summary>
/// <param name="element">The TextBox to which the attached property is written.</param>
/// <param name="value">The needed HasContextMenu value.</param>
public static void SetHasContextMenu(TextBox element, bool value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(HasContextMenuProperty, value);
}
/// <summary>
/// Identifies the HasContextMenu dependency property.
/// </summary>
public static readonly DependencyProperty HasContextMenuProperty =
DependencyProperty.RegisterAttached(
"HasContextMenu",
typeof(bool),
typeof(TextBox),
new PropertyMetadata(false, OnHasContextMenuPropertyChanged));
/// <summary>
/// HasContextMenuProperty property changed handler.
/// </summary>
/// <param name="d">TextBoxContextMenu that changed its HasContextMenu.</param>
/// <param name="e">Event arguments.</param>
private static void OnHasContextMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// code
}
I should add that the attached dependency property is defined in a class which inherits from RadContextMenu, which is a DependencyObject (I have been reading and somewhere it is suggested that this can't work if the attached property is defined in such a class, but this seems strange)
I figured it out. It was indeed due to having the attached property defined in the class I have.
To fix it, i created a new class called TextBoxContextMenuService and put the code in there instead.