Я создаю 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>
Из-за этого BindingDataContext из CustomRepeatButton является самим CustomRepeatButton элементом управления. Каждый Binding, который использует синтаксис неявной привязки ({Binding path}), теперь будет связываться с CustomRepeatButton. Это объясняет сообщение об ошибке, в котором говорится, что исходный объект имеет тип CustomRepeatButton: «Ошибка пути BindingExpression: свойство «Адрес» не найдено в «объекте» «CustomRepeatButton»».
Это также показывает, почему вы никогда не должны явно устанавливать DataContext пользовательского элемента управления, если только вы не хотите удивить пользователя своего элемента управления.
Чтобы исправить это, никогда не устанавливайте DataContext пользовательского элемента управления явно. Чтобы привязать внутренние элементы к свойствам элемента управления, вы должны использовать правильный источник привязки, то есть явный источник привязки.
Если внутренности назначены свойству ContentContentControl, например 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 или есть соответствующий код, который вы не можете показать.