2013-08-01 2 views
0

Моя цель состоит в том, чтобы одно свойство размера обновлялось двумя разными текстовыми полями. В частности, каждый компонент, подключенный к одному текстовому полю, который подсвечен в XAML ниже. В частности, линия, которая говорит: {Binding Path = Dimensions.Width, ....}Связать отдельные компоненты одного свойства с двумя отдельными элементами управления в WPF

private Size _dimension; 
    public Size Dimensions 
    { 
     get { return _dimension; } 
     set 
     { 
      _dimension = value; 
      OnPropertyChanged("Dimensions"); 
     } 
    } 

Мой XAML выглядит следующим образом:

<StackPanel HorizontalAlignment="Left" Orientation="Horizontal"> 
    <Label Content="Width" /> 
    <TextBox Width="50" Text="{Binding Path=Dimensions.Width, 
      Converter={StaticResource myStringToDoubleConverter}, 
      UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/> 
    <Label Content="Height" /> 
    <TextBox Width="50" Text="{Binding Path=Dimensions.Height, 
      Converter={StaticResource myStringToDoubleConverter}, 
      UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/> 
</StackPanel> 

Проблема у меня есть функция набора не называется когда я меняю текст в текстовом поле. Я предполагаю, что причина в том, что я не поставляю объект «Размер», но в лучшем случае - двойной, а в худшем - строку. У меня есть конвертер, чтобы преобразовать строку в double, но это не похоже на трюк. Мое чувство Multibinding дает мое решение, однако все примеры, которые я видел, объединяют две строки в третий элемент управления. Я не заинтересован в контроле с добавленными значениями, а скорее в обновлении компонентов моего «сложного» свойства.

Я понимаю, что могу создать два свойства «sub», Height и Width, а затем обновить свойство Dimension за раз, но, похоже, более элегантное решение должно существовать.

Почему мое текстовое поле не связано должным образом с одним компонентом свойства Dimension и соответствующим образом обновляет размер?

Кажется излишним создать класс-оболочку, который реализует INotifyPropertyChanged.

public class MySizeClass : Size, INotifyPropertyChanged 
+0

Я думаю, что ваш ответ является разумным решением. У вашего кода в вашем исходном посте определенно есть некоторые проблемы: 1. Вы можете установить только ширину, а не высоту. 2. Как ваш конвертер преобразует ширину в размер без знания ее высоты? Как вы сохраняете высоту от предыдущего объекта размера? –

+0

@BillZhang Конвертер фактически просто меняет String на Double, а не на размер. Чтобы преобразовать в размер, мне нужно как-то получить параметр Height. Мне непонятно, что это действительно возможно. Если мой конвертер не сделал что-то особенное, в котором он обновляет только один из двух компонентов. Что-то вроде: return (new Size (input, this.Height)), но это не особенно элегантно и, возможно, хуже ... Редактировать: Будет корректировать код в OP, чтобы быть более однородным. – mwjohnson

+0

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

ответ

3

Если тип Size, который вы используете здесь, предоставлен WPF, то есть System.Windows.Size, тогда проблема в том, что это не класс. Это тип значения (т. Е. struct). И хотя он предоставляет сеттеры для своих Width и Height, это, вероятно, не должно, потому что они не делают то, что вы ожидаете.

C# не позволит вам изменить Dimensions.Width либо - если вы пишете следующее:

src.Dimensions.Width = 123; 

где src является экземпляром типа, который содержит определение этих свойств вы показали в своей «Component Bind Solution », вы получите эту ошибку:

Cannot modify the return value of 'WpfApplication1.Source.Dimensions' because it is not a variable

C# отказывается здесь из-за той же основной вопрос, который останавливает это от работы в WPF: это потому, что Size является тип значения. Любая попытка восстановить свойство Dimensions вернет совершенно новую копию значения.

Конечно, что C# код эффективно короток для:

Size dimensions = src.Dimensions; 
dimensions.Width = 123; 

C# на самом деле позволит вам писать, но если вы попробуете, вы увидите, почему он перестал вас писать более короткую версию: src.Dimensions.Width все равно вернет старое значение.(Что этот код делает делает копию src.Dimensions, магазины, копирования в локальной переменной с именем dimensions, а затем изменяет, что локальная переменная, оставляя оригинал без изменений.)

В принципе, нет никакого способа, чтобы получить ссылку на Size экземпляр, который был возвращен вашим имуществом Dimensions - вы можете только получить копию значения. И единственный способ обновить его - написать совершенно новое Size на его месте. Вот почему вы не можете самостоятельно изменить Width или Height.

Попытка исправить это с помощью преобразователей не будет работать, потому что проблема не в том, что вы пытаетесь записать в цель. Проблема заключается в том, что вы хотите написать недоступную цель, то есть конкретное свойство типа значения. Связывание может только записываться в свойство target, если оно является свойством объекта, а не значением.

(В принципе, Microsoft может заставить WPF обнаружить это, прочитать его значение Dimensions, изменить копию полученного значения и затем записать это измененное значение обратно в Dimensions. И хотя это было бы удобно в этом случай, это также было бы значительным изменением от того, как привязка данных обычно связана с обновлением свойств. Я думаю, что, вероятно, лучше, чтобы они не пытались, потому что это могло легко маскировать другие проблемы.)

В C# вы можете пишите в индивидуальные свойства, если у вас есть локальная переменная или поле, содержащее значение Size, конечно, потому что переменная или поле обозначает конкретный экземпляр значения. Поля, локальные переменные, ref или out аргументы и элементы массива являются особыми, потому что на самом деле возможно работать со значениями, которые они хранят на месте. Но вы не можете это делать, когда проходите через свойства. По той же причине, если вы определяете List<Size>, вы не можете написать myList[0].Width = 123;, хотя это просто отлично с массивом.

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

Если WPF мог привязываться к полям, вы могли бы делать то, что хотите. Но это не так, поэтому вы не можете.

+0

Блестящий ответ, большое спасибо. Я регулярно испытываю страх благодаря знаниям и знаниям сообщества stackoverflow. Вы упомянули: «... System.Windows.Size, ... не является классом. Это тип значения (т. Е. Структура)». Будет ли этот подход работать, если бы я должен был создать новый класс MySize с двумя переменными типа double, Height и Width, а затем определить свойство Dimensions с помощью этого типа? то есть «Public MySize Dimensions» Таким образом, определение моего свойства осуществляется с помощью класса, а не встроенной структуры. – mwjohnson

+1

Да, это должно сработать. –

1

Вот Компонент Bind Solution, который приятно работает полностью нормально.

private Double m_Width; 
    public Double Width 
    { 
     get { return (Dimensions.Width); } 
     set 
     { 
      m_Width = value; 
      Dimensions= new Size(m_Width, m_Height); 
     } 
    } 
    private Double m_Height; 
    public Double Height 
    { 
     get { return (Dimensions.Height); } 
     set 
     { 
      m_Height = value; 
      Dimensions= new Size(m_Width, m_Height); 
     } 
    } 
    private Size _dimension; 
    public Size Dimensions 
    { 
     get { return _dimension; } 
     set 
     { 
      _dimension = value; 
      OnPropertyChanged("Dimensions"); 
     } 
    } 

Просто кажется, что привязка непосредственно к Dimensions.Width - это то, что будет поддерживаться WPF.

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