I'm having some trouble styling a custom control that I built. Here's the control source:
namespace SilverlightStyleTest
{
public class AnotherControl: TextBox
{
public string MyProperty { get; set; }
}
}
In the same namespace & project I try to create a style with a setter for MyProperty like so:
<UserControl x:Class="SilverlightStyleTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Local="clr-namespace:SilverlightStyleTest">
<UserControl.Resources>
<Style x:Name="AnotherStyle" TargetType="Local:AnotherControl">
<Setter Property="Width" Value="200"/>
<Setter Property="MyProperty" Value="Hello."/>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Local:AnotherControl Style="{StaticResource AnotherStyle}"/>
</Grid>
</UserControl>
I end up with the runtime error:
Invalid attribute value MyProperty for property Property. [Line: 9 Position: 30]
I can't figure out what's wrong with the style to cause this error. I also tried "fully qualifying" the property name as "Local:AnotherControl.MyProperty" but that didn't work either.
Non-dependency properties cannot be set in styles.
You need to define it as a DependencyProperty:
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(AnotherTextBox),
new FrameworkPropertyMetadata((string)null));
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
Related
I have a class Data, that has a property IsDactaCorrect. This is the objects that I use in a ObservableCollection as data source of a datagrid.
I would like to use a cell style with the rows of the datagrid, so if the property is false, show a tooltip in the cell with the data error.
For that I would like to use a attached property that binds to the IsDataProperty. This attached property is used to communicate the value with the style that is defined in a xml resoruces file. Also this style use a converter to return the text of the tooltip.
The problem is that the value that arrives to to the converter is unset.
Also I have a the same idea with the tooltip of the headers of the datagrid. This works, but this case it binds to a property in the view model of the window, so it is a different case. In this case I want to bind to a property of a the object that is data source of the row.
The code is this:
The base view model:
using System.ComponentModel;
namespace TestStyleWithAttachedProperties.ViewModels
{
public abstract class BaseViewModel : INotifyPropertyChanging, INotifyPropertyChanged
{
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Administrative Properties
/// <summary>
/// Whether the view model should ignore property-change events.
/// </summary>
public virtual bool IgnorePropertyChangeEvents { get; set; }
#endregion
#region Public Methods
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the changed property.</param>
public virtual void RaisePropertyChangedEvent(string propertyName)
{
// Exit if changes ignored
if (IgnorePropertyChangeEvents) return;
// Exit if no subscribers
if (PropertyChanged == null) return;
// Raise event
var e = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, e);
}
/// <summary>
/// Raises the PropertyChanging event.
/// </summary>
/// <param name="propertyName">The name of the changing property.</param>
public virtual void RaisePropertyChangingEvent(string propertyName)
{
// Exit if changes ignored
if (IgnorePropertyChangeEvents) return;
// Exit if no subscribers
if (PropertyChanging == null) return;
// Raise event
var e = new PropertyChangingEventArgs(propertyName);
PropertyChanging(this, e);
}
#endregion
}
}
The view that has the datagrid:
<Window x:Class="TestStyleWithAttachedProperties.MainWindowView"
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"
xmlns:vm="clr-namespace:TestStyleWithAttachedProperties.ViewModels"
xmlns:ap="clr-namespace:TestStyleWithAttachedProperties.AttachedProperties"
xmlns:local="clr-namespace:TestStyleWithAttachedProperties"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="GUIResources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
<DataGrid Name="dgdTest" Grid.Column="0" Margin="5,5,5,5"
ItemsSource="{Binding Items}"
AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Header="Price" Binding="{Binding Price}" Width="150"
ap:HeaderAttachedProperty.Tooltip="{Binding Path=DataContext.PriceTooltip, Source={x:Reference dummyElement}}"
HeaderStyle="{StaticResource DataGridColumnHeaderConTooltip}"
ap:CellWithErrorsAttachedProperty.TextoTooltip01="{Binding Path=Tooltip, Source={x:Reference dummyElement}}"
ap:CellWithErrorsAttachedProperty.TextoTooltip02="{Binding Path=ErrorDescription, Source={x:Reference dummyElement}}"
ap:CellWithErrorsAttachedProperty.EsDatoCorrecto="{Binding Path=IsDataCorrect}"
CellStyle="{StaticResource DataGridCellWithErrorsStyle}">
</DataGridTextColumn>
<DataGridTextColumn Header="Discount" Binding="{Binding Discount}" Width="150"
ap:HeaderAttachedProperty.Tooltip="Tooltip cabecera 2"
HeaderStyle="{StaticResource DataGridColumnHeaderConTooltip}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
The view model:
using System.Collections.ObjectModel;
namespace TestStyleWithAttachedProperties.ViewModels
{
public class MainWindowViewModel : BaseViewModel
{
public MainWindowViewModel()
{
Items = new ObservableCollection<Data>();
Data miData01 = new Data()
{
Price = 1m,
Discount = 0m,
Tooltip = "Tooltip Data01",
ErrorDescription = "No errors",
IsDataCorrect = true,
};
Items.Add(miData01);
Data miData02 = new Data()
{
Price = 2m,
Discount = 10m,
Tooltip = "Tooltip Data02",
ErrorDescription = "No errors",
IsDataCorrect = true,
};
Items.Add(miData02);
Data miData03 = new Data()
{
Price = -1m,
Discount = 0m,
Tooltip = "Tooltip Data03",
ErrorDescription = "Price has to be greater than 0.",
IsDataCorrect = false,
};
Items.Add(miData03);
}
private ObservableCollection<Data> _items;
public ObservableCollection<Data> Items
{
get { return _items; }
set
{
_items = value;
base.RaisePropertyChangedEvent(nameof(Items));
}
}
private string _priceTooltip = "Tooltip for Price column";
public string PriceTooltip
{
get { return _priceTooltip; }
set { _priceTooltip = value; }
}
}
}
Then converter that I want to use in the stlye:
using System;
using System.Globalization;
using System.Windows.Data;
namespace TestStyleWithAttachedProperties.Converters
{
public class CellTooltipMultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//here values are unset values.
//If I return a string, it is shown in the datagrid. But I can't do any logic because the values are null.
return "Hola";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
The attached property that I want to use to communicate the view with the style:
using System.Windows;
namespace TestStyleWithAttachedProperties.AttachedProperties
{
public static class CellWithErrorsAttachedProperty
{
//TextoTooltip01
//Primer texto del tooltip que se mostrarĂ¡.
public static readonly DependencyProperty TextoTooltip01Property =
DependencyProperty.RegisterAttached(
"TextoTooltip01",
typeof(string),
typeof(CellWithErrorsAttachedProperty));
public static string GetTextoTooltip01(DependencyObject obj)
{
return (string)obj.GetValue(TextoTooltip01Property);
}
public static void SetTextoTooltip01(DependencyObject obj, string value)
{
obj.SetValue(TextoTooltip01Property, value);
}
//TextoTooltip02
//Primer texto del tooltip que se mostrarĂ¡.
public static readonly DependencyProperty TextoTooltip02Property =
DependencyProperty.RegisterAttached(
"TextoTooltip02",
typeof(string),
typeof(CellWithErrorsAttachedProperty));
public static string GetTextoTooltip02(DependencyObject obj)
{
return (string)obj.GetValue(TextoTooltip01Property);
}
public static void SetTextoTooltip02(DependencyObject obj, string value)
{
obj.SetValue(TextoTooltip01Property, value);
}
//ES DATO CORRECTO
//Indica si el dato de la celda es correcto o no.
public static readonly DependencyProperty EsDatoCorrectoProperty =
DependencyProperty.RegisterAttached(
"EsDatoCorrecto",
typeof(bool),
typeof(CellWithErrorsAttachedProperty));
public static bool GetEsDatoCorrecto(DependencyObject obj)
{
return (bool)obj.GetValue(EsDatoCorrectoProperty);
}
public static void SetEsDatoCorrecto(DependencyObject obj, bool value)
{
obj.SetValue(EsDatoCorrectoProperty, value);
}
}
}
The class data that is source of the rows of the datagrid. I would like to bind the IsDataCorrect to the attached property.
The xml resource file with the styles:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ap="clr-namespace:TestStyleWithAttachedProperties.AttachedProperties"
xmlns:conv="clr-namespace:TestStyleWithAttachedProperties.Converters">
<conv:CellTooltipMultiValueConverter x:Key="CellTooltipMultiValueConverter"/>
<Style TargetType="{x:Type DataGridColumnHeader}" x:Key="DataGridColumnHeaderConTooltip">
<Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
<Setter Property="ToolTip" Value="{Binding Column.(ap:HeaderAttachedProperty.Tooltip), RelativeSource={RelativeSource Self}}"/>
</Style>
<Style TargetType="{x:Type DataGridCell}" x:Key="DataGridCellWithErrorsStyle">
<Setter Property="ToolTip">
<Setter.Value>
<MultiBinding Converter="{StaticResource ResourceKey=CellTooltipMultiValueConverter}">
<MultiBinding.Bindings>
<Binding Path="(ap:CellWithErrorsAttachedProperty.TextoTooltip01)" RelativeSource="{RelativeSource AncestorType=ToolTip}"/>
<Binding Path="(ap:CellWithErrorsAttachedProperty.TextoTooltip02)" RelativeSource="{RelativeSource AncestorType=ToolTip}"/>
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(ap:CellWithErrorsAttachedProperty.EsDatoCorrecto), RelativeSource={RelativeSource AncestorType=DataGridCell}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Orange"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
The attached property for show a tooltip in the header of the datagrid. Really this is not used for the problem that I have, because what I want to do it is show a tooltip in a cell, but in this way I show the complete code of the project.
using System.Windows;
namespace TestStyleWithAttachedProperties.AttachedProperties
{
public static class HeaderAttachedProperty
{
//TextoTooltip01
//Primer texto del tooltip que se mostrarĂ¡.
public static readonly DependencyProperty TooltipProperty =
DependencyProperty.RegisterAttached(
"Tooltip",
typeof(string),
typeof(HeaderAttachedProperty));
public static string GetTooltip(DependencyObject obj)
{
return (string)obj.GetValue(TooltipProperty);
}
public static void SetTooltip(DependencyObject obj, string value)
{
obj.SetValue(TooltipProperty, value);
}
}
}
I have an Observable collection SerialList of a class SerialItem containing a single string property Number, to which I am binding a DataGrid's ItemsSource. The purpose is for the user to see some preexisting string values, and add some new ones, being validated through a wpf ValidationRule. When the user enters an invalid string and presses "Enter" or tabs out or clicks on another row, the validation fails just fine, presenting the typical Error Template. Pressing "Escape" without further editing, the "wrong" value gets emptied just fine. The problem starts when an existing or already approved cell value is changed to a "wrong" one and the user presses "Enter", validation fails again as expected, but pressing "Escape" immediately after that, the "wrong" value remains, as if having passed validation.
Could anybody perhaps explain or give some pointers to why that happens, or perhaps a solution to that?
The code I used is following:
MainWindow.xaml
<Window x:Class="WpfApp8.MainWindow"
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"
xmlns:local="clr-namespace:WpfApp8"
mc:Ignorable="d"
Title="MainWindow" Height="330" Width="200">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<DockPanel>
<DataGrid DockPanel.Dock="Top" ItemsSource="{Binding SerialList}"
AutoGenerateColumns="False" RowStyle="{StaticResource RowStyle}">
<DataGrid.RowValidationRules>
<local:SerialValidationRule ValidationStep="UpdatedValue"/>
</DataGrid.RowValidationRules>
<DataGrid.Columns>
<DataGridTextColumn Header="Serial" Width="*" IsReadOnly="false" Binding="{Binding Path=Number, UpdateSourceTrigger=LostFocus}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Window>
MainWindowViewModel.cs
using System.Collections.ObjectModel;
namespace WpfApp8
{
public class MainWindowViewModel : ViewModelBase
{
public LicenseModel myModel;
private ObservableCollection<SerialItem> m_SerialList = new ObservableCollection<SerialItem>();
public MainWindowViewModel()
{
myModel = new LicenseModel {
Serial_numbers = new Serial_numbers {
SerialNumbers = new ObservableCollection<SerialItem> { new SerialItem { Number = "111111"}, new SerialItem { Number = "222222" } }
}
};
SerialList = myModel.Serial_numbers.SerialNumbers;
}
public ObservableCollection<SerialItem> SerialList
{
get
{
return m_SerialList;
}
set
{
m_SerialList = value;
myModel.Serial_numbers.SerialNumbers = m_SerialList;
RaisePropertyChanged("SerialList");
}
}
private void OnSerialListCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("SerialList");
}
}
}
DataModel.cs
using System.Collections.ObjectModel;
using System.Xml.Serialization;
namespace WpfApp8
{
[XmlRoot(ElementName = "serial_numbers")]
public class Serial_numbers
{
[XmlElement(ElementName = "number")]
public ObservableCollection<SerialItem> SerialNumbers { get; set; }
}
public class SerialItem
{
public string Number { get; set; }
}
public class LicenseModel
{
[XmlElement(ElementName = "serial_numbers")]
public Serial_numbers Serial_numbers { get; set; }
}
}
SerialValidationRule.cs
using System.Globalization;
using System.Windows.Controls;
namespace WpfApp8
{
public class SerialValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var bindExpressions = value as System.Windows.Data.BindingGroup;
var bindingitem = bindExpressions.Items[0];
var serial = bindingitem as SerialItem;
if (serial.Number == null)
{
return new ValidationResult(false, "Serial number cannot be empty");
}
if (serial.Number.Length != 6)
{
return new ValidationResult(false, "Serial number has to be 6 characters long!");
}
return new ValidationResult(true, "");
}
}
}
App.xaml
<Application x:Class="WpfApp8.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style x:Key="RowStyle" TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)/ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Application.Resources>
</Application>
I have tried to run the application with the following code and found that when we try to do escape an error value a second time.
We are not updating the source property so the validations are not happing in this case once it's cleared while escaping.
An easy approach to get the error to stay is we can add a new property HasErrors to SerialItem (ViewModel different from DataModel for binding). And instead of performing validations at the time of property update. We can try validating at the DataGrid- RowEditEnding event and update the HasErrors. Where later we can use this HasErrors property to show the styles etc.
Or instead, we can try implementing the INotifyDataErrorInfo on SerialItem. And persist those errors in a dictionary. And make use of these to show errors. Additional details related to the same can be found here:
Link1
Link2
If you are dealing with multiple properties of the SerialItem to validated. Then it would be better to make use of INotifyDataErrorInfo.
I managed to solve the issue by neutralizing the effect of the "Esc" key, using a command bount to the DataGrid's keypress event.
<DataGrid.InputBindings>
<KeyBinding Key="Esc" Command="{Binding EscCommand}"/>
</DataGrid.InputBindings>
and
...
public RelayCommand EscCommand { get; set; }
...
EscCommand = new RelayCommand(OnEscCommand, CanEscCommand);
private bool CanEscCommand()
{
return true;
}
private void OnEscCommand()
{
}
I am working a very simple lookless control, and I can't seem to get one of the template bindings to work. In the control I have two Dependency Properties, the one that is a string works, and the one that is an int does not.
The csharp code looks like this:
using System;
using System.Windows;
using System.Windows.Controls;
namespace ControlDemo
{
public class TextControlLookless : Control
{
#region Title
public static readonly DependencyProperty ChartTitleProperty =
DependencyProperty.Register("ChartTitle", typeof(string), typeof(TextControlLookless),
null);
public String ChartTitle
{
get { return (string)GetValue(ChartTitleProperty); }
set
{
SetValue(ChartTitleProperty, value);
}
}
#endregion
#region Value
public static readonly DependencyProperty ChartValueProperty =
DependencyProperty.Register("ChartValue", typeof(int), typeof(TextControlLookless),
null);
public int ChartValue
{
get { return (int)GetValue(ChartValueProperty); }
set
{
SetValue(ChartValueProperty, value);
}
}
#endregion
#region ctor
public TextControlLookless()
{
this.DefaultStyleKey = typeof(TextControlLookless);
}
#endregion
}
}
And the xaml for the control looks like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ControlDemo">
<Style TargetType="local:TextControlLookless">
<Setter Property="ChartTitle" Value="Set Title" />
<Setter Property="ChartValue" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TextControlLookless">
<Grid x:Name="Root">
<Border BorderBrush="Black" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{TemplateBinding ChartTitle}" />
<TextBlock Text="{TemplateBinding ChartValue}" Grid.Row="1" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When I put this on a page, I can see the ChartTitle (either Set Title, or whatever I set it to), but the ChartValue never shows up. If I change its type to a string, it does show up, so I must be missing something.
The problem is that TemplateBinding is a far more primitive operation than Binding. Binding is an actual class and includes some helpful features including the implicit conversion of strings back and forth between other data types.
TemplateBinding is purely a markup instruction and crucially in your case does not do type conversion for you. Hence the dependency property being bound to a Text property of a TextBlock must be a string.
You have two choices:-
One choice is instead using TemplateBinding give the TextBlock a name and assign its Text in the ChartValue property changed call back:-
#region Value
public static readonly DependencyProperty ChartValueProperty =
DependencyProperty.Register("ChartValue", typeof(int), typeof(TextControlLookless),
new PropertyMetadata(0, OnChartValuePropertyChanged));
private static void OnChartValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextControlLookless source = d as TextControlLookless;
source.Refresh();
}
public int ChartValue
{
get { return (int)GetValue(ChartValueProperty); }
set
{
SetValue(ChartValueProperty, value);
}
}
#endregion
private TextBlock txtChartValue { get; set; }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
txtChartValue = GetTemplateChild("txtChartValue") as TextBlock;
Refresh();
}
private void Refresh()
{
if (txtChartValue != null)
{
txtChartValue.Text = ChartValue.ToString();
}
}
where the xaml looks like:-
<TextBlock x:Name="txtChartValue" Grid.Row="1" />
The other choice is to create a private dependency property for the value with type of string:-
#region Value
public static readonly DependencyProperty ChartValueProperty =
DependencyProperty.Register("ChartValue", typeof(int), typeof(TextControlLookless),
new PropertyMetadata(0, OnChartValuePropertyChanged));
private static void OnChartValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetValue(ChartValueStrProperty, e.NewValue.ToString());
}
private static readonly DependencyProperty ChartValueStrProperty =
DependencyProperty.Register("ChartValueStr", typeof(string), typeof(TextControlLookless),
new PropertyMetadata("0"));
public int ChartValue
{
get { return (int)GetValue(ChartValueProperty); }
set
{
SetValue(ChartValueProperty, value);
}
}
#endregion
where the xaml looks like:-
<TextBlock Text="{TemplateBinding ChartValueStr}" Grid.Row="1" />
Note that the ChartValueStrProperty is private and I haven't bothered creating a standard .NET property to cover it. TemplateBinding actually takes the property name you assign suffixes is with "Property" then looks for a static field on the target type.
Both approaches have their strengths and weaknesses. The first approach is the more common pattern but takes a little more code and is less flexiable (the control displaying the value must be a TextBlock). The second is more flexiable and uses less code but is somewhat unorthodox.
If I have a boolean variable in a ViewModel Class, lets say
public bool test = true; (This is in C#)
Is there ANYWAY in XAML/Expression Blend to take this variable and change it to
false USING PURELY XAML, no code behind or anything?
I want to do this for a mouse over event.
If the mouse is over a certain object the boolean variable should become false, otherwise it should remain true.
Answer 1 (easiest):
Why not do this?
public bool Test
{
get { return myControl.IsMouseOver; }
}
I know you want to do it in all XAML, but since you're already declaring the property, you might as well do this instead of saying.
public bool Test = false;
Answer 2 (more code, MVVM approach which is better in the long run):
Here basically, you create a Dependency Property (called Test) on Window1, and on the XAML side, you create a style for Window1 that says that its Test property will be the same as the button IsMouseOver property (I left the myButton_MouseEnter event so you can check the state of the variable when the mouse is over the button, I checked myself and it does change to true, you can remove the MouseEnter handler, and it'll still work)
XAML:
<Window x:Class="StackOverflowTests.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" x:Name="window1" Height="300" Width="300"
xmlns:local="clr-namespace:StackOverflowTests">
<Window.Resources>
<Style TargetType="{x:Type local:Window1}">
<Setter Property="Test" Value="{Binding ElementName=myButton, Path=IsMouseOver}">
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button x:Name="myButton" Height="100" Width="100" MouseEnter="myButton_MouseEnter">
Hover over me
</Button>
</Grid>
</Window>
C#:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
public bool Test
{
get { return (bool)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
// Using a DependencyProperty as the backing store for Test. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test", typeof(bool), typeof(Window1), new UIPropertyMetadata(false));
private void myButton_MouseEnter(object sender, MouseEventArgs e)
{
bool check = this.Test;
}
}
I am trying to bind a property (Button.Background) to a property on my custom attached property.
In a C# file I have
public static class Square
{
public static readonly DependencyProperty PlayerProperty =
DependencyProperty.RegisterAttached
(
name : "Player",
propertyType : typeof(Player),
ownerType : typeof(UIElement),
defaultMetadata: new FrameworkPropertyMetadata(null)
);
public static Player GetPlayer(UIElement element)
{
return (Player)element.GetValue(PlayerProperty);
}
public static void SetPlayer(UIElement element, Player player)
{
element.SetValue(PlayerProperty, player);
}
// Other attached properties
}
A snippet of my XAML is
<Grid Name="board" Grid.Row="1" Grid.Column="1">
<Grid.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Height" Value="20" />
<Setter Property="Width" Value="20" />
<Setter Property="BorderThickness" Value="3" />
<Setter Property="Background"
Value="{Binding Path=(l:Square.Player).Brush, Mode=OneWay}" />
</Style>
</Grid.Resources>
</Grid>
This is the error I get:
Cannot convert string '(l:Square.Player).Brush' in attribute 'Path' to object of type 'System.Windows.PropertyPath'.
Property path is not valid. 'Square' does not have a public property named 'Player'.
Error at object 'System.Windows.Data.Binding' in markup file 'Gobang.Gui;component/mainwindow.xaml' Line 148 Position 59.
But since Player is an attached property which is on Square, the above code should work, right?
I believe your attached property should designate Square as the owner rather than UIElement.
public static readonly DependencyProperty PlayerProperty =
DependencyProperty.RegisterAttached("Player", typeof(Player),
typeof(Square), new FrameworkPropertyMetadata(null));
I got it to work.
Note: its a read-only property, the Helper class HAS TO inherit from DependencyObject
public class Helper : DependencyObject
{
public static readonly DependencyPropertyKey IsExpandedKey = DependencyProperty.RegisterAttachedReadOnly(
"IsExpanded", typeof(bool), typeof(Helper), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty IsExpandedProperty = IsExpandedKey.DependencyProperty;
public static bool GetIsExpanded(DependencyObject d)
{
return (bool)d.GetValue(IsExpandedKey.DependencyProperty);
}
internal static void SetIsExpanded(DependencyObject d, bool value)
{
d.SetValue(IsExpandedKey, value);
}
}
You can't set up a binding in the way that you're doing it - you'll need an instance of either Square or Player to bind to that.