2015-08-17 2 views
0

Мой сценарий состоит в том, что есть два элемента управления. Один, в котором вы устанавливаете минуты и секунды, в которых вы указываете секунды.Как связать два элемента с одним источником в WPF

Оба они должны быть привязаны к одной собственности в модели. Это свойство имеет тип string. Эта строка находится в формате [hh: mm: ss]. Таким образом, изменение значения в контроле «минут» должно изменить часть «мм» строки, а изменение значения в секундах «секунд» должно изменить часть «ss» строки.

Заранее спасибо

+1

Самый чистый способ, о котором я могу думать, состоит в том, чтобы просто иметь еще два свойства на виртуальной машине, и в их сеттерах соответствующим образом отрегулировать окончательную строку времени. Вы уверены, что не можете добавить какие-либо свойства в виртуальную машину? – user3690202

+0

Вы можете использовать два преобразователя: один, который принимает исходную строку и выводит миллиметр, а другой - выводит ss. Но, как упоминалось выше, есть 2 свойства чище. –

+0

Отлично, спасибо :) – kamilw

ответ

1

Вот рабочий раствор 3-свойство ViewModel, если вы используете TimeSpan и его диапазон составляет от 0 до 59ч 59с. Я не полностью протестировал, и условия/валидация будут меняться в зависимости от требований. Я использовал TimeSpan.TotalSeconds, потому что это необходимое нам разрешение; значение, при установке TimeSpan на новое значение, мы просто установили общее количество секунд через публичное свойство. Альтернативой может быть наличие 2 TimeSpan свойств в вашей модели ViewModel, а затем при настройке общедоступной собственности вы можете позвонить _item.TotalSeconds = VMMinutes.TotalSeconds + VMSeconds.TotalSeconds.TotalSeconds. В принципе у вас есть много вариантов дизайна здесь.

MainWindow.xaml:

MainWindow.xaml.cs:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = new ItemViewModel(new Item(new TimeSpan(0, 3, 59))); 
    } 
} 

ItemViewModel.cs:

public class ItemViewModel : INotifyPropertyChanged 
{ 
    private readonly Item _item; 

    public event PropertyChangedEventHandler PropertyChanged; 

    public ItemViewModel(Item item) 
    { 
     _item = item; 
    } 

    public string TotalSeconds 
    { 
     get 
     { 
      return _item.TotalSeconds.ToString(); 
     } 
     set 
     { 
      double newTotSecs; 
      if(!string.IsNullOrEmpty(value)) 
      { 
       if(double.TryParse(value, out newTotSecs)) 
       { 
        _item.TotalSeconds = newTotSecs; 
        NotifyPropertyChanged(); 
        NotifyPropertyChanged("Minutes"); 
        NotifyPropertyChanged("Seconds"); 
       } 
      } 
     } 
    } 
    public string Seconds 
    { 
     get 
     { 
      return (_item.TotalSeconds % 60).ToString(); 
     } 
     set 
     { 
      int newVal; 
      if(!string.IsNullOrEmpty(value)) 
      { 
       if(int.TryParse(value, out newVal)) 
       { 
        if(newVal >= 0 && newVal <= 59) 
        { 
         int totMinSec; 
         if(int.TryParse(Minutes, out totMinSec)) 
         { 
          _item.TotalSeconds = (totMinSec * 60) + newVal; 
          NotifyPropertyChanged(); 
          NotifyPropertyChanged("TotalSeconds"); 
         } 
        } 
       } 
      } 
     } 
    } 

    public string Minutes 
    { 
     get 
     { 
      return ((int)(_item.TotalSeconds/60)).ToString(); 
     } 
     set 
     { 
      int newVal; 
      if(!string.IsNullOrEmpty(value)) 
      { 
       if(int.TryParse(value, out newVal)) 
       { 
        if(newVal >= 0 && newVal <= 59) 
        { 
         int totSec; 
         if(int.TryParse(Seconds, out totSec)) 
         { 
          _item.TotalSeconds = totSec + (newVal * 60); 
          NotifyPropertyChanged(); 
          NotifyPropertyChanged("TotalSeconds"); 
         } 
        } 
       } 
      } 
     } 
    } 

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

Item.cs:

public class Item 
{ 
    private TimeSpan _time; 
    public double TotalSeconds 
    { 
     get 
     { 
      return _time.TotalSeconds; 
     } 
     set 
     { 
      if(value >= 0) 
      { 
       _time = new TimeSpan(0, 0, (int)value); 
      } 
     } 
    } 
    public Item(TimeSpan time) 
    { 
     _time = time; 
    } 
} 

enter image description here

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

+0

Где смысл в этих вложенных блоках? Вы знаете, что существуют логические операторы типа '&&'? – Clemens

+1

Конечно, да. Иллюстрирует логические шаги. Если OP требует комментариев о том, что происходит, гораздо проще добавить их выше if-statement, а не комментировать правую сторону для каждого условия. Мне жаль, что мой стиль вам не нравится, но когда половина сообщества не знает таблицы истинности/булевой алгебры, гораздо проще проиллюстрировать, что вы делаете, не объединяя все возможное. – Kcvin

1

Я бы использовал подход NETScape выше, но инкапсулировал его в пользовательский элемент управления. Пользовательский элемент управления XAML будет что-то вроде:

<UserControl> 
    <Grid x:Name="LayoutRoot"> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="*"/> 
      <RowDefinition Height="*"/> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"/> 
      <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 
     <TextBlock Text="Minutes" Grid.Row="0" Grid.Column="0"/> 
     <TextBox Text="{Binding InternalMinutes}" Grid.Row="0" Grid.Column="1"/> 

     <TextBlock Text="Seconds" Grid.Row="1" Grid.Column="0"/> 
     <TextBox Text="{Binding InternalSeconds}" Grid.Row="1" Grid.Column="1"/> 
    </Grid> 
</UserControl> 

Затем в коде-позади, вы бы Dependency Property для фактического DateTime объекта и свойства для связывания с (вы можете использовать модель представления для этого, или просто уйти от TextChanged. Когда все его логика просмотра, это нормально!).

Пример недвижимость будет:

public int InternalSeconds 
{ 
    get { return ExternalTime.Seconds; } 
    set 
    { 
     ExternalTime.Seconds = value; 
     NotifyPropertyChanged(); 
    } 
} 

Опять же, есть несколько подходов здесь, вы можете использовать конвертер для того, чтобы использовать промежуточный объект. ExternalTime - это DP здесь, обязательно обработайте его событие Changed, если вы ожидаете, что значение изменится за пределами этого элемента управления.

Смежные вопросы