Я создаю UserControl с 2 RepeatButtons и одним TextBox. Итак, проблемы начинаются, когда я хочу привязать некоторые свойства....
К вашему сведению, я использую Caliburn.micro в качестве Framework..
мне нужно иметь значение свойства Interval от родителя, чтобы определить промежуток времени для обеих кнопок повтора
мне нужно определить MaxValue в родительском элементе и использовать его в событии MouseClick customcontrol
мне нужно инициализировать значение TextBox пользовательского элемента управления из родителя, использовать его в событии щелчка мыши пользовательского элемента управления, чтобы получить новое значение, обновить TextBox и использовать новое значение, измененное в родительском...
что я тестировал:
CustomRepeatButton.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace ChromiumWPF.UserControls
{
public partial class CustomRepeatButton : UserControl
{
public CustomRepeatButton()
{
InitializeComponent();
}
public string TextValue
{
get { return (string)GetValue(TextValueProperty); }
set { SetValue(TextValueProperty, value); }
}
public static DependencyProperty TextValueProperty =
DependencyProperty.Register("TextValue", typeof(string), typeof(CustomRepeatButton));
public int IntervalValue
{
get { return (int)GetValue(IntervalValueProperty); }
set { SetValue(IntervalValueProperty, value); }
}
public static DependencyProperty IntervalValueProperty =
DependencyProperty.Register("IntervalValue",
typeof(int),
typeof(CustomRepeatButton),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
public int MaxValue
{
get { return (int)GetValue(MaxValueProperty); }
set { SetValue(MaxValueProperty, value); }
}
public static DependencyProperty MaxValueProperty =
DependencyProperty.Register("MaxValue", typeof(int), typeof(CustomRepeatButton));
:
:
//same definition for ResetValue, MinValue and IncrementValue
private void RepeatButton_Click(object sender, RoutedEventArgs e)
{
// click on button + or - and change the value of TextValue i trap in usercontrol Parent
var RB = sender as RepeatButton;
}
}
}
CustomRepeatButton.xaml (см. Bindings TextValue и IntervalValue в стиле)
<UserControl x:Class = "ChromiumWPF.UserControls.CustomRepeatButton"
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:local = "clr-namespace:ChromiumWPF.UserControls"
mc:Ignorable = "d"
DataContext = "{Binding RelativeSource = {RelativeSource Self}}"
d:DesignHeight = "450" d:DesignWidth = "800">
<UserControl.Resources>
<Style TargetType = "TextBox">
<Style.Setters>
<Setter Property = "FontFamily" Value = "Consolas"/>
<Setter Property = "FontWeight" Value = "Medium"/>
<Setter Property = "VerticalContentAlignment" Value = "Center"/>
<Setter Property = "IsReadOnly" Value = "True"/>
<Setter Property = "IsEnabled" Value = "True"/>
<Setter Property = "Padding" Value = "5"/>
<Setter Property = "Background" Value = "Aquamarine"/>
<Setter Property = "Text" Value = "{Binding TextValue, Mode=TwoWay}"/>
</Style.Setters>
</Style>
<Style TargetType = "RepeatButton">
<Style.Setters>
<Setter Property = "Interval" Value = "{Binding IntervalValue, Mode=TwoWay}"/>
<Setter Property = "Background" Value = "Gray"/>
<EventSetter Event = "Click" Handler = "RepeatButton_Click"/>
</Style.Setters>
</Style>
</UserControl.Resources>
<Grid>
<StackPanel Orientation = "Horizontal">
<RepeatButton Content = "-" />
<TextBox />
<RepeatButton Content = "+" />
</StackPanel>
</Grid>
</UserControl>
ChromeViewModel.cs
private string adress;
public string Adress
{
get { return adress; }
set
{
adress = value;
NotifyOfPropertyChange(() => Adress);
}
}
private int _test;
public int Test
{
get { return _test; }
set
{
_test = value;
NotifyOfPropertyChange(() => Test);
}
}
private int _maxV;
public int MaxV
{
get { return _maxV; }
set
{
_maxV = value;
NotifyOfPropertyChange(() => MaxV);
}
}
ChromeView.xaml
<UserControl x:Class = "ChromiumWPF.Views.ChromeView"
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:cal = "http://www.caliburnproject.org"
xmlns:uc = "clr-namespace:ChromiumWPF.UserControls"
xmlns:local = "clr-namespace:ChromiumWPF.Views"
xmlns:vm = "clr-namespace:ChromiumWPF.ViewModels"
mc:Ignorable = "d" d:DataContext = "{d:DesignInstance Type=vm:ChromeViewModel}"
d:DesignHeight = "800" d:DesignWidth = "1200">
<Grid ShowGridLines = "True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width = "*"/>
<ColumnDefinition Width = "auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column = "0" Orientation = "Vertical" VerticalAlignment = "Top"
HorizontalAlignment = "Center">
<ComboBox x:Name = "Months" Background = "Aqua" Height = "30" Width = "100"
VerticalContentAlignment = "Center"/>
<uc:CustomRepeatButton TextValue = "{Binding Adress}"
IncrementValue = "10"
IntervalValue = "{Binding Test}"
MaxValue = "{Binding MaxV}"
MinValue = "900"
ResetValue = "1080" />
</StackPanel>
</Grid>
</UserControl>
Итак, у меня проблема с Test, Adress и MaxV (only Bindings in relation with the customControl
).
Я не понимаю, почему привязки ошибочны, определение DP и свойств кажется мне нормальным ?? это проблема DataContext? Может быть, мне нужно установить DataContext в ChromeViewModel? если да, как я мог это сделать?... Спасибо за помощь..
отображаются ошибки:
System.Windows.Data Error: 40 : BindingExpression path error: 'Adress' property not found on 'object' ''CustomRepeatButton' (Name='')'. BindingExpression:Path=Adress; DataItem='CustomRepeatButton' (Name=''); target element is 'CustomRepeatButton' (Name=''); target property is 'TextValue' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'Test' property not found on 'object' ''CustomRepeatButton' (Name='')'. BindingExpression:Path=Test; DataItem='CustomRepeatButton' (Name=''); target element is 'CustomRepeatButton' (Name=''); target property is 'IntervalValue' (type 'Int32')
System.Windows.Data Error: 40 : BindingExpression path error: 'MaxV' property not found on 'object' ''CustomRepeatButton' (Name='')'. BindingExpression:Path=MaxV; DataItem='CustomRepeatButton' (Name=''); target element is 'CustomRepeatButton' (Name=''); target property is 'MaxValue' (type 'Int32')
Между прочим, это не "таможенный контроль". это UserControl
. Я не пытаюсь быть здесь анальным, но разница важна.
@Joe Я создал TextBox только с именем Adress
, и Caliburn выполняет эту работу (связывает), и да, все в порядке
и да, вы правы, я работаю над UserControl...
В WPF система свойств зависимостей использует приоритет значений. Например, значение DataContext
для FrameworkElement
неявно наследуется от родителя. Если вы установите значение явно, вы переопределите унаследованное значение (родительский DataContext
будет игнорироваться системой свойств).
В общем, вы хотите, чтобы пользовательский элемент управления наследовал родителей DataContext
, чтобы привязки внешних данных назначались свойствам зависимостей.
Внешний Binding
использует текущий DataContext
в качестве источника (неявно):
<UserControl>
<!-- Binding expects the DataContext of CustomRepeatButton
to be of type ChromeViewModel -->
<uc:CustomRepeatButton TextValue = "{Binding Adress}" />
</UserControl>
Следующее локальное Binding
переопределяет унаследованное значение DataContext
(взятое из вашего кода):
<UserControl x:Class = "ChromiumWPF.UserControls.CustomRepeatButton"
DataContext = "{Binding RelativeSource = {RelativeSource Self}}">
</UserControl>
Из-за этого Binding
DataContext
из CustomRepeatButton
является самим CustomRepeatButton
элементом управления. Каждый Binding
, который использует синтаксис неявной привязки ({Binding path}
), теперь будет связываться с CustomRepeatButton
. Это объясняет сообщение об ошибке, в котором говорится, что исходный объект имеет тип CustomRepeatButton
: «Ошибка пути BindingExpression: свойство «Адрес» не найдено в «объекте» «CustomRepeatButton»».
Это также показывает, почему вы никогда не должны явно устанавливать DataContext
пользовательского элемента управления, если только вы не хотите удивить пользователя своего элемента управления.
Чтобы исправить это, никогда не устанавливайте DataContext
пользовательского элемента управления явно. Чтобы привязать внутренние элементы к свойствам элемента управления, вы должны использовать правильный источник привязки, то есть явный источник привязки.
Если внутренности назначены свойству Content
ContentControl
, например UserControl
, вы должны использовать Binding.RelativeSource
и установить свойство RelativeSource.AncestorType расширения RelativeSource
:
<UserControl>
<RepeatButton Interval = "{Binding RelativeSource = {RelativeSource AncestorType=UserControl}, Path=IntervalValue}" />
</UserControl>
Привязки одинаковы при определении в Style
:
<Style TargetType = "RepeatButton">
<Setter Property = "Interval"
Value = "{Binding RelativeSource = {RelativeSource AncestorType=UserControl}, Path=IntervalValue}"/>
</Style>
Если внутренние элементы определены внутри ControlTemplate
, вам нужно будет использовать расширение TemplateBinding (вместо Binding
) или использовать расширение Binding
и установить свойство Binding.RelativeSource
с помощью расширения RelativeSource и установить его RelativeSource.Mode. свойство RelativeSourceMode.TemplatedParent:
<UserControl>
<UserControl.Template>
<ControlTemplate TargetType = "CustomRepeatButton">
<!-- Recommended using the TemplateBinding extension -->
<RepeatButton Interval = "{TemplateBinding IntervalValue}" />
<!-- Using the Binding together with the RelativeSource extension -->
<RepeatButton Interval = "{Binding RelativeSource = {RelativeSource TemplatedParent}, Path=IntervalValue}" />
</ControlTemplate>
</UserControl.Template>
</UserControl>
Спасибо за ваш ответ, я должен читать медленно и проверять, чтобы понять...!! one
, я вынужден отложить определение Datacontext
из CustomRepeatButton.xaml
файла, two
значит мой подход с привязкой внутри Style не годится? я должен использовать ControlTemplate? так что мне нужно время, чтобы прочитать эту новую концепцию для меня...
1) правильно 2) Нет, я пытался сказать, что привязка к DataContext неверна. Вы должны спроектировать свой элемент управления так, как будто нет DataContext. Вместо этого привязывайтесь непосредственно к свойствам пользовательского элемента управления. Я показал два сценария: а) элемент управления — это UserControl, а макет определяется как содержимое (как в вашем случае) или б) макет определяется как ControlTemplate. Поскольку Style для RepeatButton применяется в исходном контексте (местоположении в визуальном дереве), вы определяете привязки так же, как если бы вы определяли их локально. Просто используйте RelativeSource.AncestorType
, как показано в моем примере.
Я обновил ответ, чтобы показать, как выглядит привязка внутри стиля (выглядит точно так же).
Привязка TwoWay
к RadioButton.Interval
избыточна. RadioButton
никогда не изменит значение свойства Interval
. В противном случае он был бы настроен на привязку TwoWay
по умолчанию.
Вы можете (и должны) писать <Style> <Setter ... /> </Style>
вместо <Style> <Style.Setters> <Setter ... /> </Style.Setters> </Style>
, как будто вы пишете не <UserControl> <UserControl.Content> <RadioButton /> </UserControl.Content> </UserControl>
, а компактную форму <UserControl> <RadioButton /> </UserControl>
.
Это работает, потому что класс Style
определил свойство Setters
как свойство содержимого ([ContentProperty("Setters")]
). Когда вы украшаете свой класс ContenPropertyAttribute, вы можете определить свойство, которому присваивается значение XAML корня. В этом случае каждый Setter
, который является прямым дочерним элементом корневого тега Style
, будет неявно назначен коллекции Setters
. Для UserControl
атрибут настроен как [ContentProperty("Content")]
, чтобы неявно присвоить дочерний узел корня свойству Content
.
Например, если вы настроите свой класс CustomRepeatButton для использования свойства TextValue в качестве свойства содержимого ([ContentProperty("TextValue")]
), то следующий текст «example text» будет неявно назначен свойству TextValue
: <CustomRepeatButton>example text</CustomRepeatButton>
.
Спасибо за педагогический ответ. Ваш ответ очень полный и очень интересный, и он позволяет мне еще больше улучшить и понять WPF. Еще раз спасибо.
Проблема проста. Вы пытаетесь привязать XAML элемента управления к его собственным свойствам зависимостей, но делаете это неправильно. Вам нужно установить DataContext для первого визуального элемента в элементе управления, а не для самого элемента управления.
Итак, сделайте следующее:
Сначала перейдите к файлу CustomRepeatButton.xaml. Перейдите к строке 8 (показанной ниже), где вы устанавливаете DataContext
на себя. Удалить эту строку
DataContext = "{Binding RelativeSource = {RelativeSource Self}}"
Далее переходим к первому визуальному элементу. Grid
. Присвойте ему значение x:Name
, чтобы вы могли ссылаться на него в коде программной части. Так:
<Grid x:Name = "MainGrid" ShowGridLines = "True">
Наконец, перейдите в код элемента управления за конструктором. Вручную установите DataContext
сразу после звонка на IntializeComponent
. Так сделай так, чтобы это выглядело так
public CustomRepeatButton()
{
InitializeComponent();
MainGrid.DataContext = this;
}
Это должно дать вам то, что вы хотите.
В качестве временного теста поставьте
TextBlock
в ChromeView.xaml рядом сCustomRepeatButton
. Попробуйте связать его свойство Text с «Адресом», как вы это делаете дляCustomRepeatButton
. Вы получаете ту же ошибку привязки? Я знаю, что вы сказали только привязку в отношении пользовательского элемента управления), но я просто хочу быть уверенным. Если вы получаете ту же ошибку, значит, у вас проблема с DataContext в ChromeView или есть соответствующий код, который вы не можете показать.