Свойство зависимости пользовательского элемента управления не привязывается в Silverlight - PullRequest
0 голосов
/ 15 сентября 2011

Я создал пользовательский элемент управления, например числовое обновление, следующим образом

 <StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
    <TextBox x:Name="InputTextBox" Grid.Row="0" Grid.Column="0" Grid.RowSpan="1"
                Style="{StaticResource NumericUpDownTextBoxStyle}"

             KeyDown="InputTextBox_KeyDown" 
             KeyUp="InputTextBox_KeyUp"
             GotFocus="InputTextBox_GotFocus" 
             LostFocus="InputTextBox_LostFocus"  
             MouseWheel="InputTextBox_MouseWheel"
             MouseEnter="InputTextBox_MouseEnter"
             LayoutUpdated="InputTextBox_LayoutUpdated" 
             Text="{Binding Path=ControlValue, Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,NotifyOnValidationError=True}"/>
</StackPanel>

Я привязал ViewModel к этому элементу управления, где я устанавливаю свойство ControlValue в свойство TextBox текстового поля шаблона пользовательского элемента управления.

Все работает нормально на уровне управления. Я выставил из usercontrol.

public static readonly DependencyProperty MaximumValueProperty;

public static readonly DependencyProperty MinimumValueProperty;

public static readonly DependencyProperty StepValueProperty;

public static readonly DependencyProperty TextValueProperty;

Мои свойства

 public double Maximum
{
    get
    {
        return (double)GetValue(MaximumValueProperty);
    }
    set
    {
        SetValue(MaximumValueProperty, value);
        this.ViewModel.Maximum = this.Maximum;
    }
}

public double Minimum
{
    get
    {
        return (double)GetValue(MinimumValueProperty);
    }
    set
    {
        SetValue(MinimumValueProperty, value);
        this.ViewModel.Minimum = this.Minimum;
    }
}

public double Step
{
    get
    {
        return (double)GetValue(StepValueProperty);
    }
    set
    {
        SetValue(StepValueProperty, value);
        this.ViewModel.Step = this.Step;
    }
}

public double TextValue
{
    get
    {
        return (double)GetValue(TextValueProperty);
    }
    set
    {
        SetValue(TextValueProperty, value);
        this.ViewModel.ControlValue = Convert.ToString(value);
    }
}

Инициализация свойства.

  static NumericUpDown()
    {
        MaximumValueProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
        MinimumValueProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
        StepValueProperty = DependencyProperty.Register("Step", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
        TextValueProperty = DependencyProperty.Register("TextValue", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
    }

Моя реализация Usercontrol на странице MainPage.xaml выглядит следующим образом

 <local:NumericUpDown Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding ElementName=FranePrice, Path=DataContext.FranePrice}"></local:NumericUpDown>

Где у меня есть другая ViewModel, которую я связываю со страницей XAML, и есть свойство в ViewModel, которое я связываю со свойством TextValue Usercontrol.

FramePrice - это свойство в модели View, которое я связываю со свойством TextValue пользовательского элемента управления

и XAML главной страницы -

<UserControl x:Class="DatePicker.MainPage"
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:DatePicker"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel Orientation="Vertical">

        <local:NumericUpDown Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding ElementName=FranePrice, Path=DataContext.FranePrice}"></local:NumericUpDown>
        <Button Content="Show Date" Height="23" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click"/>
    </StackPanel>
</Grid>

Это View модель страницы, где я использовал пользовательский контроль. При событии щелчка я показываю TextValue пользователю.

открытый класс MainPageViewModel: EntityViewModel {

    public MainPageViewModel()
    {

    }
    private double framePrice;
    public Double FramePrice
    {
        get
        {
            return framePrice;
        }
        set
        {
            framePrice = value;
            PropertyChangedHandler("FramePrice");
        }
    }
}

Когда я изменяю TextValue в пользовательском элементе управления, он не изменяется в свойстве FramePrice модели представления страницы.

Что-то не так в коде. ???

Согласно сообщению Люка Вудворда , я обновил код следующим образом

 public static readonly DependencyProperty MaximumValueProperty;
    public static readonly DependencyProperty MinimumValueProperty;
    public static readonly DependencyProperty StepValueProperty;
    public static readonly DependencyProperty TextValueProperty;

    public static double Max;
    public static double Min;
    public static double Stp;
    public static double Val;

  public double Maximum
    {
        get
        {
            return (double)GetValue(MaximumValueProperty);
        }
        set
        {
            SetValue(MaximumValueProperty, value);
        }
    }

    public double Minimum
    {
        get
        {
            return (double)GetValue(MinimumValueProperty);
        }
        set
        {
            SetValue(MinimumValueProperty, value);
        }
    }

    public double Step
    {
        get
        {
            return (double)GetValue(StepValueProperty);
        }
        set
        {
            SetValue(StepValueProperty, value);
        }
    }

    public double TextValue
    {
        get
        {
            return (double)GetValue(TextValueProperty);
        }
        set
        {
            SetValue(TextValueProperty, value);
        }
    }
  static NumericUpDown()
    {
        MaximumValueProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onMaximumValueChanged)));
        MinimumValueProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onMinimumValueChanged)));
        StepValueProperty = DependencyProperty.Register("Step", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onStepValueChanged)));
        TextValueProperty = DependencyProperty.Register("TextValue", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onTextValueChanged)));
    }

    private static void onStepValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Stp = (double)e.NewValue;
    }
    private static void onMinimumValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Min = (double)e.NewValue;
    }
    private static void onMaximumValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Max = (double)e.NewValue;
    }
    private static void onTextValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Val = (double)e.NewValue;
    }

Затем я получил доступ к Max, Min, Stp и Val в модели представления пользовательского элемента управления для выполнения моей логики.

и код XAML следующий

<local:NumericUpDown x:Name="ctlUpDown" Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding Path=FramePrice}"></local:NumericUpDown>

и XAML пользовательского управления

<StackPanel Margin="5" Orientation="Horizontal" VerticalAlignment="Center">
    <TextBox x:Name="InputTextBox" Grid.Row="0" Grid.Column="0" Grid.RowSpan="1"
             Height="23" VerticalAlignment="Top" 
             Width="50" TextAlignment="Center"  
             KeyDown="InputTextBox_KeyDown" 
             KeyUp="InputTextBox_KeyUp"
             GotFocus="InputTextBox_GotFocus" 
             LostFocus="InputTextBox_LostFocus"  
             MouseWheel="InputTextBox_MouseWheel"
             MouseEnter="InputTextBox_MouseEnter"
             Text="{Binding Path=TextValue, ElementName=ctlUpDown,  Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,NotifyOnValidationError=True}"
             />
</StackPanel>

Ответы [ 2 ]

1 голос
/ 15 сентября 2011

Первое, что я заметил неправильно в вашем коде, это свойства Maximum, Minimum, Step и TextValue.Вот свойство TextValue:

public double TextValue
{
    get
    {
        return (double)GetValue(TextValueProperty);
    }
    set
    {
        SetValue(TextValueProperty, value);
        this.ViewModel.ControlValue = Convert.ToString(value);
    }
}

Свойства, которые поддерживаются свойством зависимости, таким как четыре, которые я упомянул выше, должны ВСЕГДА выглядеть следующим образом:

public double TextValue
{
    get { return (double)GetValue(TextValueProperty); }
    set { SetValue(TextValueProperty, value); }
}

Другими словами, метод get не должен содержать ничего, кроме вызова GetValue, а метод set должен содержать не что иное, как вызов SetValue.

Причина этого заключается в том, что когда Silverlight изменяетзначение свойства зависимостей TextValue, оно не будет использоваться с помощью указанного выше свойства.Значения свойств зависимостей хранятся в системе зависимостей Silverlight, и когда Silverlight хочет изменить значение одного из них, он направляется непосредственно в эту систему зависимостей.Это не вызывает ваш код вообще.Свойства, подобные описанным выше, предоставляются только для вашего удобства, предоставляя вам простой способ доступа и изменения значения, хранящегося в свойстве зависимости.Они никогда не будут вызываться ничем, кроме вашего собственного кода.

Как правило, если вы хотите, чтобы метод вызывался при изменении значения свойства зависимости, вам нужно передать PropertyChangedCallback в PropertyMetadata при регистрации свойства зависимости,Однако я подозреваю, что в вашем случае вам не нужно этого делать.

Мне кажется, у вас есть три свойства:

  • свойство FramePrice в вашей модели представленияclass,
  • свойство зависимостей TextValue вашего пользовательского элемента управления NumericUpDown,
  • свойство зависимостей Text TextBox в XAML вашего пользовательского элемента управления NumericUpDown.

У меня сложилось впечатлениечто вы хотите, чтобы свойство FramePrice в вашей модели представления всегда имело то же значение, что и свойство Text TextBox.Чтобы сделать это, вам нужно привязать свойство FramePrice к свойству TextValue объекта NumericUpDown, а затем привязать его к свойству Text TextBox.

Чтобы связать первые два из этих свойств вместе, есть паравещи измениться.Во-первых, свойство TextValue в вашем элементе <local:NumericUpDown> должно выглядеть следующим образом:

TextValue="{Binding Path=FramePrice}"

Привязка {Binding ElementName=FramePrice, Path=DataContext.FramePrice} не будет работать, поскольку в вашем XAML нет элемента с атрибутом x:Name="FramePrice".Значение свойства ElementName в {Binding ...} должно совпадать с x:Name объекта в XAML.

Вам также необходимо настроить DataContext для главной страницы.Если ваш объект модели представления главной страницы имеет конструктор с нулевым аргументом, один из способов сделать это состоит в следующем: этот ответ .

Чтобы связать два вторых свойства вместе, я бы:

  • добавьте атрибут x:Name к элементу <UserControl> вашего элемента управления NumericUpDown (скажем, x:Name="ctlUpDown"),
  • замените свойство Text TextBox в вашем элементе управления NumericUpDown со следующим:

    Text="{Binding Path=TextValue, ElementName=ctlUpDown, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True}"/>
    

После этого вы можете удалить все строки this.ViewModel.SomeProperty = ... из своего класса code-behind.Они не нужны, и, как я уже объяснил, они не будут запущены, когда вы этого хотели.

Наконец, есть ли причина, по которой вы не используете элемент управления NumericUpDown набора инструментов Silverlight?

РЕДАКТИРОВАТЬ 2 .взгляните на один из двух загруженных вами проектов Silverlight (я проигнорировал тот, в котором _2).Это очень мало похоже на ваш вопрос.

Я могу только предположить, что вы хотите, чтобы два текстовых поля (одно из которых находится в пользовательском элементе управления) всегда имели одинаковое значение.Я смог сделать это после внесения следующих изменений:

  • MainPageViewModel.cs: добавить ClearErrorFromProperty("DPropertyBind"); в установщик свойств.(В противном случае ошибка проверки никогда не удаляется.)

  • MyUserControlWVM.xaml: удалена ссылка на обработчик события LostFocus, добавлена ​​привязка к свойству Text и добавлен атрибут x:Name к атрибуту <UserControl> элемент.Другими словами, теперь это выглядит следующим образом:

    <UserControl x:Class="DependencyPropertyBinding.MyUserControlWVM"
        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"
        x:Name="ctlWVM"
        d:DesignHeight="300" d:DesignWidth="205">
        <StackPanel Orientation="Horizontal" Width="204" Height="32">
            <TextBox x:Name="textbox" Height="30" Width="200" Text="{Binding Path=DProperty, ElementName=ctlWVM, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
        </StackPanel>
    </UserControl>
    
  • MyUserControlWVM.xaml.cs: переименованное свойство зависимости DependencyPropertyValue в DPropertyProperty (соглашение об именах таково, что поле static readonly имеет имя свойства (в данном случае DProperty) с Property добавлено). Я также удалил обработчик событий TextBox_LostFocus.

0 голосов
/ 15 сентября 2011

Если приведенный выше код точен, вы написали FramePrice как FranePrice в привязке

В окне вывода должно отображаться это как ошибка привязки, когда страницазагружено.

в настоящее время Binding ElementName=FranePrice, Path=DataContext.FranePrice

должно быть: Binding ElementName=FramePrice, Path=DataContext.FramePrice

«С большими возможностями связывания сопряжена большая ответственность» :)

...