I am trying to set up a custom style for my newly made usercontrol, however i am getting the error : "Cannot convert the value in attribute 'Property' to object of type 'System.Windows.DependencyProperty'."
I thought i had set up Dependency properties but it seemed this was not the case, so i did some research and added:
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(BitmapSource), typeof(Image));
to make this:
-- MyButton.Xaml.Cs --
namespace Client.Usercontrols
{
public partial class MyButton : UserControl
{
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(BitmapSource), typeof(Image));
public MyButton()
{
InitializeComponent();
}
public event RoutedEventHandler Click;
void onButtonClick(object sender, RoutedEventArgs e)
{
if (this.Click != null)
this.Click(this, e);
}
BitmapSource _imageSource;
public BitmapSource ImageSource
{
get { return _imageSource; }
set
{
_imageSource = value;
tehImage.Source = _imageSource;
}
}
}
}
This unfortunately does not work. I also tried this:
public BitmapSource ImageSource
{
get { return (BitmapSource)GetValue(MyButton.ImageSourceProperty); }
set
{
SetValue(ImageSourceProperty, value);
}
}
But that did not work and the image was not shown and generated the same error as mentioned previously anyway.
Any ideas?
Regards Kohan.
-- MyButton.Xaml --
<UserControl x:Class="Client.Usercontrols.MyButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" MinHeight="30" MinWidth="40"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Button Width="Auto" HorizontalAlignment="Center" Click="onButtonClick">
<Border CornerRadius="5" BorderThickness="1" BorderBrush="Transparent" >
<Grid>
<Image Name="tehImage" Source="{Binding ImageSource}" />
<TextBlock Name="tehText" Text="{Binding Text}" Style="{DynamicResource ButtonText}" />
</Grid>
</Border>
</Button>
</UserControl>
-- MYButton Style --
<Style TargetType="{x:Type my:MyButton}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type my:MyButton}">
<ContentPresenter />
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="ImageSource" Value="../Images/Disabled.png" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Biggest problem I see is that you're registering the property as owned by Image rather than by your UserControl. Change to:
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(BitmapSource), typeof(MyButton));
If that doesn't work, will need to see your XAML.
The standard form for a dependency property is (i've added in your information):
public BitmapSource ImageSource
{
get { return (BitmapSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
/* Using a DependencyProperty as the backing store for ImageSource.
This enables animation, styling, binding, etc... */
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource",
typeof(BitmapSource),
typeof(MyButton),
new UIPropertyMetadata(null)
);
it seems like your also trying to pass through the dependency property to the ImageSource of the object called "tehImage". You can set this up to automatically update using the PropertyChangedCallback... this means that whenever the property is updated, this will call the update automatically.
thus the property code becomes:
public BitmapSource ImageSource
{
get { return (BitmapSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
/* Using a DependencyProperty as the backing store for ImageSource.
This enables animation, styling, binding, etc... */
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource",
typeof(BitmapSource), typeof(MyButton),
new UIPropertyMetadata(null,
ImageSource_PropertyChanged
)
);
private static void ImageSource_PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((MyButton)source).tehImage.ImageSource = (ImageSource)e.NewValue
}
Hopefully with the correctly registered dependency property, this will help you narrow down the issue (or even fix it)
Set the DataContext for your UserControl:
public MyButton()
{
InitializeComponent();
DataContext = this;
}
Alternatively, if you can't do that (since the DataContext is set to another object, for example), you can do this in your XAML:
<UserControl x:Class="Client.Usercontrols.MyButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" MinHeight="30" MinWidth="40"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
x:Name="MyControl">
<Button Width="Auto" HorizontalAlignment="Center" Click="onButtonClick">
<Border CornerRadius="5" BorderThickness="1" BorderBrush="Transparent" >
<Grid>
<Image Name="tehImage" Source="{Binding ElementName=MyControl, Path=ImageSource}" />
<TextBlock Name="tehText" Text="{Binding ElementName=MyControl, Path=Text}" Style="{DynamicResource ButtonText}" />
</Grid>
</Border>
</Button>
</UserControl>
The correct way of implementing a source for an Image in a user control in my opinion is not BitmapSouce. The easiest and best way (according to me again) is using Uri.
Change your dependency property to this (while also defining a change callback event):
ImageSourceProperty = DependencyProperty.Register(
"ImageSource", typeof (Uri), typeof (MyButton),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnImageSourceChanged)));
and the property to this:
public Uri ImageSource
{
get
{
return (Uri)GetValue(ImageSourceProperty);
}
set
{
SetValue(ImageSourceProperty, value);
}
}
Where your call back is like this:
private static void OnImageSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyButton hsb = (MyButton)sender;
Image image = hsb.tehImage;
image.Source = new BitmapImage((Uri) e.NewValue);
}
Related
I have a dependency property in a UserControl with a property called SelectedColor. From my main app, the view of the window that uses this my code is:
<controls:ColorPicker SelectedColor="{Binding MyCanvas.CanvasBackgroundColor}" />
And the code from the view model is:
public MyCanvas { get; set; }
public MyWindowViewModel(MyCanvas myCanvas)
{
MyCanvas = myCanvas;
}
And then the XAML for my UserControl is:
<UserControl . . .>
<Button Click="Button_Click">
<Button.Style>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{Binding SelectedColor}" BorderBrush="Black" BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</UserControl>
And the code-behind:
public ColorPicker()
{
InitializeComponent();
DataContext = this;
}
public SolidColorBrush SelectedColor
{
get { return (SolidColorBrush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register(nameof(SelectedColor), typeof(SolidColorBrush), new UIPropertyMetadata(null));
I think the problem might be with the line in the code-behind DataContext = this;. Is it correct that declaring this creates an entirely new context for the instance of this user control in the main app and therefore any values sent to it from the view model would be re-initialized? If so, how can I send the value over without it being re-declared? I also need the DataContext = this line because without it some functionality within my UserControl will no longer work.
Has anyone encountered this before?
Thanks in advance!
DataContext = this sets the DataContext of the UserControl to itself. You don't want to do this. Instead you could bind to a property of the UserControl using a {RelativeSource} without setting the DataContext property:
<Border Background="{Binding SelectedColor, RelativeSource={RelativeSource AncestorType=UserControl}}"
BorderBrush="Black" BorderThickness="1" />
Code-behind:
public ColorPicker()
{
InitializeComponent();
}
public SolidColorBrush SelectedColor
{
get { return (SolidColorBrush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register(nameof(SelectedColor), typeof(SolidColorBrush), new UIPropertyMetadata(null));
In my WPF Custom Control which draws a pie chart, I successfully made it draw a pie chart given a set of values in a string, for example "10 20 30" would draw a pie chart with correct proportions. I bound the DrawingImage's drawing property to a converter to convert from the string into a DrawingGroup. This worked great, but I am trying to bypass the need for a converter.
Here is my MainWindow:
<Grid Margin="10">
<local:PieChart DrawingCode="289 666 1337 780" Width="400" Height="400" RingWidth="300" Background="White" />
</Grid>
Here is my template for the custom control:
<Style TargetType="{x:Type local:PieChart}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PieChart}">
<Grid>
<Image Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<Image.Source>
<DrawingImage Drawing="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DrawingCode}" />
</Image.Source>
</Image>
<Ellipse Width="{TemplateBinding RingWidth}" Height="{TemplateBinding RingWidth}" Fill="{TemplateBinding Background}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And finally, here is my attempt at the Custom Control class:
public class PieChart : Control
{
public static readonly DependencyProperty DrawingCodeProperty = DependencyProperty.Register("DrawingCode", typeof(string), typeof(PieChart), new UIPropertyMetadata(null));
public static readonly DependencyProperty RingWidthProperty = DependencyProperty.Register("RingWidth", typeof(double), typeof(PieChart), new UIPropertyMetadata(null));
static PieChart()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PieChart), new FrameworkPropertyMetadata(typeof(PieChart)));
}
public string DrawingCode
{
get { return DrawingCodeConverter((string)GetValue(DrawingCodeProperty)); }
set { SetValue(DrawingCodeProperty, value); }
}
public double RingWidth
{
get { return (double)GetValue(RingWidthProperty); }
set { SetValue(RingWidthProperty, this.Width - value); }
}
public DrawingGroup DrawingCodeConverter(string value)
{
// This converter works but is long so I removed it from the post.
}
}
I am pretty sure the problem is somewhere in the data types I should be using. Also, if there is a completely different way to do this that I am ignorant of, please let me know. Also note that RingWidth is not the problem, it is DrawingCode.
The getter and setter of the CLR wrapper of a dependency property may be bypassed when the property is accessed in XAML or by a Binding, Style Setter, Animation, etc. WPF then calls GetValue and SetValue directly. The reason is explained in XAML Loading and Dependency Properties
You must therefore not call anything else than GetValue and SetValue in the getter and setter. Instead, declare your dependency property with a PropertyChangedCallback like this:
public static readonly DependencyProperty DrawingCodeProperty =
DependencyProperty.Register(
"DrawingCode",
typeof(string),
typeof(PieChart),
new FrameworkPropertyMetadata(DrawingCodePropertyChanged));
public string DrawingCode
{
get { return (string)GetValue(DrawingCodeProperty); }
set { SetValue(DrawingCodeProperty, value); }
}
private static void DrawingCodePropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var pieChart = (PieChart)o;
pieChart.SetDrawingCode((string)e.NewValue);
}
private void SetDrawingCode(string drawingCode)
{
var drawingGroup = DrawingCodeConverter(drawingCode);
// do something with drawingGroup
}
Can anyone help me with this problem , because i read a blog how to do this on
Walkthrough: Two-way binding inside a XAML User Control
but i don't now how to do this with a bool value
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValueIsSelected(IsSelectedProperty, value); }
}
private void SetValueIsSelected(DependencyProperty property, object value,
[System.Runtime.CompilerServices.CallerMemberName] bool s = null)
{
SetValue(property, value);
if (PropertyChanged != null)
{
string sender = s.ToString();
PropertyChanged(this, new PropertyChangedEventArgs(sender));
}
}
// Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(CustomPolygon), new PropertyMetadata(0));
So this was wrong accordingly #Clemens
to understand it more here i some more information on my application
In my MainWindow i am using two ComboBoxes to fill my data with binding to a public ObservableCollection DataPlannen
My code behind MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private ObservableCollection<Plan> dataPlannen;
public ObservableCollection<Plan> DataPlannen
{
get { return dataPlannen; }
set
{
if (value != dataPlannen)
{
dataPlannen = value;
}
}
}
//non relevant code deleted
//get database data for ComboBoxes
private void btnGetPlanData_Click(object sender, RoutedEventArgs e)
{
try
{
this.Cursor = Cursors.Wait;
DataPlannen = new PlanCanvasModel().PlanHotspotsHardwares;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString();
}
finally
{
this.Cursor = Cursors.Arrow;
}
}
private void cmbHotspot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cmbHotspot.SelectedIndex != -1)
{
foreach (Hotspot hotspot in cmbHotspot.ItemsSource)
{
if (hotspot == (Hotspot)cmbHotspot.SelectedItem)
{
hotspot.IsSelected = true;
else
{
hotspot.IsSelected = false;
}
}
}
}
My MainWindow XAML:
<Grid Background="LightGray">
<DockPanel Name="TestCanvas" LastChildFill="True">
<Grid x:Name="Sidebar" DockPanel.Dock="Right" Width="200">
<StackPanel Margin="0,10,10,0">
<ScrollViewer Height="112" VerticalScrollBarVisibility="Auto">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Button x:Name="btnGetPlanData" Width="30" Height="30" HorizontalAlignment="Left" Margin="5,0" Click="btnGetPlanData_Click">
<Image Source="Images/database38.png" Stretch="Uniform"></Image>
<Button.ToolTip>Laad plannen in</Button.ToolTip>
</Button>
</StackPanel>
<Grid>
<ComboBox x:Name="cmbPlannen" Width="180"
ItemsSource="{Binding ElementName=myWindow,Path=DataPlannen}"
DisplayMemberPath="{Binding Plan_naam}"
SelectedValuePath="{Binding Plan_Id}"
SelectionChanged="cmbPlannen_SelectionChanged" IsEditable="True">
<ComboBox.ToolTip>
<ToolTip>Zoek op text of selecteer</ToolTip>
</ComboBox.ToolTip>
</ComboBox>
</Grid>
</StackPanel>
</ScrollViewer>
<Grid>
<ComboBox Margin="5" SnapsToDevicePixels="True" ItemsSource="{Binding ElementName=cmbPlannen,Path=SelectedItem.Hotspots,Mode=TwoWay}"
x:Name="cmbHotspot" SelectedIndex="0"
DisplayMemberPath="{Binding Hotspot_naam}"
SelectedValuePath="{Binding Hotspot_Id}"
SelectedItem="{Binding SelectedItem}"
SelectionChanged="cmbHotspot_SelectionChanged" IsEditable="True">
<ComboBox.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0"></Setter>
<Style.Triggers>
<Trigger Property="ComboBoxItem.IsSelected" Value="True">
<Setter Property="ComboBoxItem.Background" Value="LightGray" />
</Trigger>
<Trigger Property="ComboBoxItem.IsHighlighted" Value="True">
<Setter Property="ComboBoxItem.Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ToolTip>
<ToolTip>Zoek op text of selecteer</ToolTip>
</ComboBox.ToolTip>
</ComboBox>
</Grid>
</StackPanel>
</Grid>
<Grid x:Name="MainContainer" ClipToBounds="True"
>
<Viewbox>
<ItemsControl x:Name="drawingsheet" ItemsSource="{Binding ElementName=cmbPlannen, Path=SelectedItem.Hotspots}"
Width="{StaticResource canvasWidth}"
Height="{StaticResource canvasHeight}"
MouseLeftButtonUp="drawingsheet_MouseLeftButtonUp"
MouseRightButtonDown="drawingsheet_MouseRightButtonDown"
MouseWheel="drawingsheet_MouseWheel"
MouseLeftButtonDown="drawingsheet_MouseLeftButtonDown"
MouseMove="drawingsheet_MouseMove"
Background="White"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{Binding ElementName=cmbPlannen,Path=SelectedItem.Plan_image,Converter={StaticResource ResourceKey=bytesToBitmapImageConverter}}"
Width="{StaticResource canvasWidth}" Height="{StaticResource canvasHeight}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<customPolygon:CustomPolygon x:Name="currentPolygon" PointsPolygon="{Binding Hotspot_points}"
IsSelected="{Binding IsSelected,Mode=TwoWay}"
HasChildren="{Binding HasChildren}"></customPolygon:CustomPolygon>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleCanvas"></ScaleTransform>
<TranslateTransform x:Name="moveCanvas"></TranslateTransform>
</TransformGroup>
</ItemsControl.RenderTransform>
</ItemsControl>
</Viewbox>
</Grid>
</DockPanel>
</Grid>
My UserControl CustomPolygon.Xaml:
<UserControl x:Class="testCanvas.Controls.DrawingControls.CustomPolygon"
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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Name="userControl"
>
<Polygon x:Name="polygon"
Points="{Binding ElementName=userControl,Path=PointsSource}"
StrokeThickness="0.5"
Stroke="Black"
Opacity="0.5"
MouseEnter="polygon_MouseEnter"
MouseLeave="polygon_MouseLeave"
MouseLeftButtonDown="polygon_MouseLeftButtonDown"/>
</UserControl>
My code behind from CustomPolygon
public partial class CustomPolygon : UserControl, INotifyPropertyChanged
{
public CustomPolygon()
{
InitializeComponent();
}
#region Properties
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
SetValue(IsSelectedProperty, value);
if (IsSelected)
{
polygon.Stroke = Brushes.Red;
polygon.StrokeThickness = 2;
}
else
{
polygon.Stroke = Brushes.Black;
polygon.StrokeThickness = 0.5;
}
}
}
// Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
//points for polygon
public PointCollection PointsPolygon
{
get { return (PointCollection)GetValue(PointsPolygonProperty); }
set { SetValue(PointsPolygonProperty, value); }
}
// Using a DependencyProperty as the backing store for PointsPolygon. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointsPolygonProperty =
DependencyProperty.Register("PointsPolygon", typeof(PointCollection), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool HasChildren
{
get { return (bool)GetValue(HasChildrenProperty); }
set { SetValue(HasChildrenProperty, value); }
}
// Using a DependencyProperty as the backing store for HasChildren. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasChildrenProperty =
DependencyProperty.Register("HasChildren", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
#endregion
private void polygon_MouseEnter(object sender, MouseEventArgs e)
{
if (IsSelected != true)
{
polygon.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(Properties.Settings.Default.HotspotHover));
polygon.StrokeThickness = 1;
}
}
private void polygon_MouseLeave(object sender, MouseEventArgs e)
{
if (IsSelected != true)
{
polygon.Stroke = Brushes.Black;
polygon.StrokeThickness = 0.5;
}
}
private void polygon_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
IsSelected = !IsSelected;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Problem is now if i change my selection in cmbHotspot the property is not changing in my usercontrol and visa versa
What they are telling in this blog is not correct for WPF. The article is not about WPF and what it shows is not even necessary in Windows Runtime. You should definitely ignore the parts about setting the DataContext in the constructor of a custom control.
Anyway, you must not call anything else than SetValue in the setter of the CLR wrapper of a dependency property. See the XAML Loading and Dependency Properties article on MSDN for details.
Besides that a dependency property does not need to raise a PropertyChanged event, so your SetValueIsSelected property is redundant anyway.
Finally, your property metadata was wrong, because 0 is not a valid default value for type bool.
Your declaration should look like this:
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(CustomPolygon));
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
Unless you don't want to set any non-standard property metadata, you don't need to specify the PropertyMetadata parameter of the Register method.
However, if you want that the property binds two-way by default, you have to set the appropriate flag by property metadata.
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(
false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
I have just started to learn WPF.
I have a button with image. like Image+Text
<Button Height="67" Name="Button1" Width="228" HorizontalContentAlignment="Left">
<StackPanel Orientation="Horizontal" >
<Image Source="Images/add.png" Stretch="Uniform"></Image>
<TextBlock Text=" Create Company" VerticalAlignment="Center" FontSize="20"></TextBlock>
</StackPanel>
</Button>
Now I want to add many more buttons in the above format.
So I have to write the same code again and again.
So I decided to have a customButton to do my job easily.
I tried to create the custom control.
I added a property named Image there.
Now how should I give value to that property?
Am I going on the wrong way?
Here you have tutorial how to create a custom control.
[1.] Add new item "Custom Control (WPF)" with name "ButtonImg".
After this step, VS create for you two files: "ButtonImg.cs" and "/Themes/Generic.xaml".
[2.] Add few dependency properties to "ButtonImg.cs" file:
I created properties to: image source, text, image width and height.
public class ButtonImg : Control
{
static ButtonImg()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ButtonImg), new FrameworkPropertyMetadata(typeof(ButtonImg)));
}
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ButtonImg), new PropertyMetadata(null));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ButtonImg), new PropertyMetadata(string.Empty));
public double ImageWidth
{
get { return (double)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register("ImageWidth", typeof(double), typeof(ButtonImg), new PropertyMetadata((double)30));
public double ImageHeight
{
get { return (double)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
public static readonly DependencyProperty ImageHeightProperty =
DependencyProperty.Register("ImageHeight", typeof(double), typeof(ButtonImg), new PropertyMetadata((double)30));
}
[3.] In this step you must create Template for your new custom control. So you must edit following file "/Themes/Generic.xaml":
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfButtonImg">
<Style TargetType="{x:Type local:ButtonImg}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ButtonImg}">
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="{TemplateBinding ImageSource}"
Height="{TemplateBinding ImageHeight}" Width="{TemplateBinding ImageWidth}"
Stretch="Uniform" />
<TextBlock Text="{TemplateBinding Text}" Margin="10,0,0,0" VerticalAlignment="Center" FontSize="20" />
</StackPanel>
</Button.Content>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
[4.] Example of using this new custom control is following:
First you must add appropriate namespace:
xmlns:MyNamespace="clr-namespace:WpfButtonImg"
Now you can use it like this:
<MyNamespace:ButtonImg ImageSource="/Images/plug.png" Text="Click me!" />
I am not sure what I am doing wrong here. I spent a good hour last night to figure it out, maybe I am just dumb.
I created this user control to display a bordered text, which uses data binding to fill the style and the text.
This is how I call it from the main page:
<mynamespace:BorderedText x:Name="DateTime"
Grid.Column="1"
Grid.Row="0"
BorderStyle="{StaticResource borderStyle}"
LabelStyle="{StaticResource labelStyle}"
TextStyle="{StaticResource valueStyle}"
Label="Current Date/Time"
Text="N/A" />
The control is pretty simple:
<UserControl x:Class="MyNamespace.BorderedText"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="480"
d:DesignWidth="480">
<Grid>
<Border Name="border" Style="{Binding BorderStyle}">
<StackPanel>
<TextBlock Style="{Binding LabelStyle}"
Text="{Binding Label}" />
<TextBlock Style="{Binding TextStyle}"
Text="{Binding Text}" />
</StackPanel>
</Border>
</Grid>
The problem is that all data binding works, except for the Border data binding. I also tried to data bind the background or any other property, without success.
Code behind has the DependencyProperty properties set up and that’s it. Note that the DataContext for data binding is set up in the constructor. Tried to assign it to the Grid or to the Border itself, without success.
Does anybody have any clue or see something big I am missing here?
namespace MyNamespace
{
public partial class BorderedText : UserControl
{
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(BorderedText), new PropertyMetadata(null));
public static readonly DependencyProperty LabelStyleProperty = DependencyProperty.Register("LabelStyle", typeof(Style), typeof(BorderedText), new PropertyMetadata(null));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(BorderedText), new PropertyMetadata(null));
public static readonly DependencyProperty TextStyleProperty = DependencyProperty.Register("TextStyle", typeof(Style), typeof(BorderedText), new PropertyMetadata(null));
public static readonly DependencyProperty BorderStyleProperty = DependencyProperty.Register("BorderStyle", typeof(Style), typeof(BorderedText), new PropertyMetadata(null));
public BorderedText()
{
InitializeComponent();
((Grid)this.Content).DataContext = this;
//((Border)this.Content).DataContext = this;
}
public string Label
{
set { SetValue(LabelProperty, value); }
get { return (string)GetValue(LabelProperty); }
}
public Style LabelStyle
{
set { SetValue(LabelStyleProperty, value); }
get { return (Style)GetValue(LabelStyleProperty); }
}
public string Text
{
set { SetValue(TextProperty, value); }
get { return (string)GetValue(TextProperty); }
}
public Style TextStyle
{
set { SetValue(TextStyleProperty, value); }
get { return (Style)GetValue(TextStyleProperty); }
}
public Style BorderStyle
{
set { SetValue(BorderStyleProperty, value); }
get { return (Style)GetValue(BorderStyleProperty); }
}
}
}
---- UPDATE:
It turned out to be something completely different and unrelated to databinding which is properly wired...
In the borderStyle I was using this syntax for a background property:
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush>
<SolidColorBrush.Color>
<Color>
<Color.A>
100
</Color.A>
<Color.R>#95</Color.R>
<Color.B>#ED</Color.B>
</Color>
</SolidColorBrush.Color>
</SolidColorBrush>
</Setter.Value>
</Setter>
which apparently works in the designer but not in the phone.
Changing it to:
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#649500ED" />
</Setter.Value>
</Setter>
Solved the problem
Well, you forgot one thing... the DataContext of the Border!
Give your UserControl a name, and then you can add to your binding something like:
<TextBox Text="{Binding Path=MyText, ElementName=UserControlRoot}" />
this will work (at least it worked for me in WPF, heh)