WPF Databinding привязка TextBox к целочисленному свойству в другом объекте
У меня есть очень простой вопрос, связанный с данными (я все еще новичок в WPF). У меня есть класс (упрощенный для этого вопроса)
public class ConfigurationData
{
public int BaudRate { get; set; }
}
В MainWindow.Xaml.cs у меня есть закрытая переменная-член:
private ConfigurationData m_data;
и метод
void DoStuff()
{
// do a bunch of stuff (read serial port?) which may result in calling...
m_data.BaudRate = 300; // or some other value depending on logic
}
В моем графическом интерфейсе MainWindow я хотел бы иметь TextBox, который отображает m_data.BaudRate AND и допускает двухстороннее связывание. Пользователь должен иметь возможность ввести значение в текстовое поле, а в текстовом поле должны отображаться новые значения, вызванные методом "DoStuff()". Я видел множество примеров привязки к другому свойству элемента управления в MainWindow и привязки к коллекции данных, но примеров привязки к свойству другого объекта не было. Я думаю, что мой пример настолько прост, насколько это возможно, с неприятным раздражением, связанным с целым числом, а не со строкой, и, если возможно, я бы хотел, чтобы пользователь мог вводить только целые числа.
Кстати, я подумал об использовании числового вверх / вниз, но решил не делать этого, так как, похоже, не было большой поддержки / примеров некоммерческих числовых элементов управления вверх / вниз. Плюс, это может быть ужасно большой диапазон чисел, через которые можно вращаться.
Я думаю, что указатель на один хороший пример поможет мне в моем пути. Большое спасибо заранее, Дейв
3 ответа
Я думаю, что это поможет вам http://blogs.msdn.com/b/wpfsdk/archive/2006/10/19/wpf-basic-data-binding-faq.aspx
Хотя этот вопрос старый, он хороший, и на него редко дается краткий ответ. Позвольте мне предложить упрощенное решение для тех, кто заходит на эту страницу.
Для поддержки двустороннего связывания, ваш начальный ConfigurationData
класс должен быть расширен для поддержки изменений свойств. В противном случае изменения в DoStuff()
не будет отражено в текстовом поле пользовательского интерфейса. Вот типичный способ сделать это:
using System.ComponentModel;
public class ConfigurationData : INotifyPropertyChanged
{
private int _BaudRate;
public int BaudRate
{
get { return _BaudRate; }
set { _BaudRate = value; OnPropertyChanged("BaudRate"); }
}
//below is the boilerplate code supporting PropertyChanged events:
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Я решил поместить привязку текстового поля прямо в XAML (я также добавил кнопку в DoStuff), вот так:
<Canvas>
<TextBox Width="96" Name="textBox1" Text="{Binding BaudRate}" />
<Button Width="96" Canvas.Top="25" Content="ChangeNumber" Click="DoStuff"/>
</Canvas>
Хитрая часть склеивает все это вместе. Для этого вам нужно определить ваш DataContext. Я предпочитаю делать это в конструкторе моего главного окна. Вот код:
public partial class MainWindow : Window
{
private ConfigurationData m_data;
public MainWindow()
{
InitializeComponent();
m_data = new ConfigurationData();
this.DataContext = m_data; // This is the glue that connects the
// textbox to the object instance
}
private void DoStuff(object sender, RoutedEventArgs e)
{
m_data.BaudRate += 300;
}
}
Я уверен, что есть лучший способ (пожалуйста, скажите мне!), Но вот один способ, который я собрал вместе, который работает. Кажется, должен быть более легкий путь. Для свойства BaudRate используйте:
public int BaudRate
{
get
{
return m_baudRate;
}
set
{
if (value != m_baudRate)
{
m_baudRate = value;
OnPropertyChanged("BaudRate");//OnPropertyChanged definition left out here, but it's pretty standard
}
}
}
Для XAML у меня нет существенной разметки:
<TextBox Height="23" Margin="137,70,21,0" Name="textBox1" VerticalAlignment="Top" />
Теперь это грязная часть... Создать класс для проверки:
public class IntRangeRule : ValidationRule
{
// See ValidationRule Class
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
try
{
if (value is int) // replace with your own logic or more robust handling...
{
return new ValidationResult(true, "Fine");
}
else
{
return new ValidationResult(false, "Illegal characters or ");
}
}
catch (Exception e)
{
return new ValidationResult(false, "Illegal characters or " + e.Message);
}
}
}
И тогда в конструкторе Window1 (MainWindow) есть:
Binding myBinding = new Binding("BaudRate");
myBinding.NotifyOnValidationError = true;
myBinding.Mode = BindingMode.TwoWay;
ValidationRule rule = new IntRangeRule();
myBinding.ValidationRules.Add(rule);
myBinding.Source = m_data; // where m_data is the member variable of type ConfigurationData
textBox1.SetBinding(TextBox.TextProperty, myBinding);
Все мои попытки сделать все в разметке провалились. Лучшие способы?
Дейв
Есть ли более простой способ без определения такого общедоступного класса и его собственных методов? Я спрашиваю, потому что, когда я использовал текстовое поле вместе с ползунком, на самом деле я могу связать имя ползунка как имя элемента вместе с моим текстовым полем.
Мой код похож:
<TextBox Text="{Binding ElementName=slValue, Path=Value, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" TextAlignment="Right" Width="40" />
<Slider Maximum="255" TickPlacement="BottomRight" TickFrequency="5" IsSnapToTickEnabled="True" Name="slValue" />
Таким образом, должно быть что-то уже существующее, например, оно используется из ползунка.
Все, что мне нужно сделать, это просто присвоить значение "slValue" из моего исходного кода, текстовое поле получило уведомление и обновилось автоматически, не определяя ничего подобного классу.