Привязка данных к методу в WPF - PullRequest
13 голосов
/ 21 февраля 2010

У меня проблемы с привязкой свойства TextBox.Text к методу объекта. Идея состоит в том, чтобы позволить пользователю записать TextBox имя файла и затем TextBlock вывести расширение этого файла.

class GetFileInfo
{
    public string GetFileExtension(string fileName)
    {
        return Path.GetExtension(fileName);
    }
}

Вот мой XAML:

<Window.Resources>
    <ObjectDataProvider x:Key="getFileInfo" MethodName="GetFileExtension" ObjectType="{x:Type local:GetFileInfo}">
        <ObjectDataProvider.MethodParameters>
            <sys:String>abc.text</sys:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

<StackPanel>
    <TextBox Name="textBox1">
        <TextBox.Text>
            <Binding Source="{StaticResource getFileInfo}" Path="MethodParameters[0]" BindsDirectlyToSource="True" UpdateSourceTrigger="PropertyChanged" />
        </TextBox.Text>
    </TextBox>
    <TextBlock Name="textBlock1" Text="{Binding Source={StaticResource getFileInfo}}"/>
</StackPanel>

По какой-то причине он ничего не делает. Кто-нибудь может указать на причины? Вот что я вижу на конструкторе и когда запускаю приложение:

alt text

И вот что происходит, когда я пытаюсь установить другой текст во время выполнения:

alt text Вот ошибка, выдаваемая de debugger при попытке установить другой текст во время выполнения:

System.Windows.Data Ошибка: 8: Невозможно сохранить значение из цели обратно в источник. BindingExpression: Path = MethodParameters [0]; DataItem = 'ObjectDataProvider' (HashCode = 2207369); целевым элементом является TextBox (Name = 'textBox1'); Свойство target имеет значение «Text» (тип «String»). ArgumentException: «System.ArgumentException: объект типа« MS.Internal.Data.PropertyPathWorker + IListIndexerArg »нельзя преобразовать в тип« System.Int32 ». в System.RuntimeType.TryChangeType (значение объекта, связыватель связываний, культура CultureInfo, логические потребности в SpecialSast) в System.RuntimeType.CheckValue (значение объекта, связыватель Binder, культура CultureInfo, invindingAttr BindingFlags) в System.Reflection.MethodBase.CheckArguments (параметры Object [], связыватель Binder, BindingFlags invokeAttr, культура CultureInfo, подпись sig) в System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, Binder Binder, параметры Object [], CultureInfo culture, логическое skipVisibilityChecks) в System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, Binder binder, Object [] параметры, CultureInfo culture) в System.Reflection.RuntimePropertyInfo.SetValue (Объект obj, Значение объекта, BindingFlags invokeAttr, Связыватель привязки, Индекс объекта [], Культура CultureInfo) в MS.Internal.Data.PropertyPathWorker.SetValue (Элемент объекта, Значение объекта) в MS.Internal.Data.ClrBindingWorker.UpdateValue (значение объекта) в System.Windows.Data.BindingExpression.UpdateSource (значение объекта) '

Ответы [ 5 ]

7 голосов
/ 22 февраля 2010

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

Создайте класс с двумя свойствами, Filename и Extension. Filename это просто свойство чтения / записи строки. Extension является строковым свойством только для чтения, метод получения которого вызывает метод, который вы пытаетесь вызвать.

Теперь заставьте этот класс реализовать INotifyPropertyChanged, потому что если свойство может измениться в коде, ему нужен способ сообщить привязке, что он это сделал. Заставьте установщик свойства Filename уведомлять привязки об изменении свойства Extension.

Добавьте Binding к TextBox, который привязывается к свойству Filename в режиме TwoWay. Добавьте Binding к TextBox, который привязывается к Extension, используя режим OneWay по умолчанию.

Последовательность событий:

  1. Пользователь вводит новый Filename в поле TextBox и нажимает клавишу TAB.
  2. TextBox теряет фокус.
  3. Поскольку режим Binding - TwoWay, и он использует поведение по умолчанию для обновления источника, когда цель теряет фокус, это то, что она делает.
  4. Binding обновляет источник, вызывая установщик Filename.
  5. Сеттер Filename поднимает PropertyChanged.
  6. Binding обрабатывает PropertyChanged, просматривает его аргумент и видит, что свойство Extension изменилось.
  7. Binding вызывает получателя свойства Extension.
  8. Получатель свойства Extension вызывает метод для определения расширения для Filename и возвращает его Binding.
  9. Binding обновляет свою цель TextBox новым значением Extension.

Это основная концепция, лежащая в основе привязки данных и шаблона MVVM. Как только вы это понимаете, это становится второй натурой, и разработка WPF становится легче примерно в десять миллионов раз.

2 голосов
/ 21 февраля 2010

Похоже, вам нужно понять MVVM, посмотрите эту классическую статью http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

1 голос
/ 21 февраля 2010

Привязка данных требует вызова события NotifyPropertyChanged при обновлении источника. В этом случае вы хотите обернуть этот вызов функции в get / set следующим образом:



public class FileWrapper: System.ComponentModel.INotifyPropertyChanged{
    private string m_filename;

    public string FileExtension{
        get{ return GetFileExtension(FileName);}
    }

    public string FileName{
        get{ return m_filename;}
        set{ m_filename = value; OnPropertyChanged("FileName"); OnPropertyChanged( "FileExtension");
    }

    public string GetFileExtension( string filename){
        //implementation
    }

    public event System.ComponentModel.NotifyPropertyChangedEvent PropertyChanged = (a,b)=>{};

    protected void OnPropertyChanged(string property){
        PropertyChanged( this, new System.ComponentModel.PropertyChangedEventArgs( property ));
    }
}

0 голосов
/ 21 февраля 2010

Хорошо, похоже, это ошибка при запуске WPF 4.0, что можно увидеть в комментариях здесь .

Woops, я был немного поспешным .. пример работает отлично, пока вы компилируете его для платформы 3.5 (в VS 2010). Но если вы преобразуете его в проект WPF 4.0, метод WeightOnPlanet в ObjectDataProvider будет больше вызываться на odp2 при редактировании текстового поля. Я пытался найти какие-либо новые атрибуты в привязке или ObjectDataProvider - но до сих пор ничего не получилось ...

При компиляции в 3.5 здесь все работает нормально.

0 голосов
/ 21 февраля 2010

Установлено ли DataContext? В своем коде позади вы установили для TextBlock значение «saadsas» (я могу только догадываться), что нарушило вашу привязку данных?

...