Привязка к свойству пользовательского элемента управления, содержащего логику, со свойством в модели представления - PullRequest
2 голосов
/ 06 мая 2011

Я создаю приложение, используя WPF и MVVM.Я столкнулся с ситуацией, когда у меня есть представление, содержащее пользовательский контроль (представляющий таймер).Этот usercontrol имеет свойство в своем коде, которое выполняет некоторые вычисления перед получением и настройкой данных.

TimerControl.xaml.cs:

public DateTime? DateTimeValue
    {
        get
        {
            string hours = this.txtHours.Text;
            string minutes = this.txtMinutes.Text;
            string amPm = this.txtAmPm.Text;
            if (!string.IsNullOrWhiteSpace(hours) && !string.IsNullOrWhiteSpace(minutes) && !string.IsNullOrWhiteSpace(amPm))
            {
                string value = string.Format("{0}:{1} {2}", this.txtHours.Text, this.txtMinutes.Text, this.txtAmPm.Text);
                DateTime time = DateTime.Parse(value);                    
                return time;
            }
            else
            {
                return null;
            }
        }
        set
        {                
            DateTime? time = value;
            if (time.HasValue)
            {
                string timeString = time.Value.ToShortTimeString();
                //9:54 AM 
                string[] values = timeString.Split(':', ' ');
                if (values.Length == 3)
                {
                    this.txtHours.Text = values[0];
                    this.txtMinutes.Text = values[1];
                    this.txtAmPm.Text = values[2];
                }
            }                
        }
    }

Теперь я хотел связать это свойство со свойствомпредставьте в представлении модель представления.Ниже приводится свойство в виртуальной машине:

public DateTime? StartTime
{  
        get  
        {                
            return _StartTime;
        }

        set
        {
            _StartTime = value;
            RaisePropertyChanged("StartTime");
        }
} 

Вот как я выполняю привязку в xaml View.

MyView.xaml:

<my:TimeControl  Background="White" Grid.Column="1" Grid.Row="2" Margin="3" x:Name="StartTimeControl" DateTimeValue="{Binding StartTime}"  Width="150" Height="26" HorizontalAlignment="Left">

Но этовыдает мне сообщение об ошибке: «Binding» не может быть установлен в свойстве «DateTimeValue» типа «TimeControl».«Привязка» может быть установлена ​​только для свойства DependencyProperty объекта DependencyObject.

Я часами пытался найти способ заставить эту привязку работать.Я даже пытался создать свойство зависимости в коде TimeControl для свойства DateTimeValue, которое разрешило указанное выше исключение, но привязка по-прежнему не работает.Всякий раз, когда я получаю доступ к свойству StartTime в коде виртуальной машины, оно показывает нулевое значение.Хотя оно должно показывать правильное значение, получая свойство DateTimeValue.

Пожалуйста, предложите мне способ сделать эту работу.Благодарю.

Ответы [ 2 ]

2 голосов
/ 06 мая 2011

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

Но вы упомянули, что пытались использовать свойство зависимости безуспешно. Я полагаю, причина в конфликте DataContexts, и ваш XAML выглядит так:

<UserControl x:Class="Test.SomeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:self="clr-namespace:Test"
             Name="Root">
    <WrapPanel>
        <self:TimerControl Time="{Binding StartTime}"/>
    </WrapPanel>
</UserControl>

Этот код не работает. Зачем? DataContext TimerControl наследуется (или, может быть, вы вообще его заменяете), в то время как когда вы обращаетесь к StartTime, вы имеете в виду ViewModel как DataContext. Таким образом, вы должны четко указать правильный DataContext:

 <self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/> 

=== ОБНОВЛЕНИЕ ===

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

public partial class Timer : UserControl
{
    public Timer()
    {
        InitializeComponent();
    }

    public DateTime? Time
    {
        get
        {
            return (DateTime?)this.GetValue(Timer.TimeProperty);
        }
        set
        {
            this.SetValue(Timer.TimeProperty, value);
        }
    }

    public static readonly DependencyProperty TimeProperty =
        DependencyProperty.Register("Time",
            typeof(DateTime?),
            typeof(Timer),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (d, e) => { }));

    private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (DateTime.Now.Ticks % 2 == 0)
        {
            this.Time = DateTime.Now;
        }
        else
        {
            this.Time = null;
        }
    }

}

И XAML:

<UserControl x:Class="Test.Timer">
    <Grid>
        <TextBox TextChanged="TextBox_TextChanged"/>
    </Grid>
</UserControl>

Использование контроля времени в XAML:

<UserControl x:Class="Test.StartupView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:self="clr-namespace:Test"
             Name="Root">
    <WrapPanel>
        <self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/>
    </WrapPanel>
</UserControl> 

Код позади StartupView:

    public StartupView()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
    }

Свойство во ViewModel остается прежним. Во время отладки сеттер свойства StartTime срабатывает каждый раз, когда я меняю текст в Timer.

2 голосов
/ 06 мая 2011

Что именно вы хотите сделать?

Нельзя связать со стандартным свойством. Если вы хотите связать, вы должны использовать свойство зависимости.

public DateTime? DateTimeValue
{
    get { return (DateTime?)GetValue(DateTimeValueProperty); }
    set { SetValue(DateTimeValueProperty, value); }
}

// Using a DependencyProperty as the backing store for DateTimeValue.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty DateTimeValueProperty =
    DependencyProperty.Register("DateTimeValue", typeof(DateTime?), typeof(TimeControl), new UIPropertyMetadata(null));

Внутри UserControl:

<TextBox Text="{Binding DateTimeValue,RelativeSource={RelativeSource AncestorLevel=1, Mode=FindAncestor,AncestorType=UserControl}, Converter=...}" />

Прямая привязка к DateTimeValue невозможна, поскольку для string-> DateTime нет доступного конвертера, поэтому вам нужно написать IValueConverter и указать это в привязке.

Конечно, снаружи вы должны иметь возможность связать значение напрямую.

...