Привязка к статическому свойству
Мне трудно связать простое статическое свойство строки с текстовым полем.
Вот класс со статическим свойством:
public class VersionManager
{
private static string filterString;
public static string FilterString
{
get { return filterString; }
set { filterString = value; }
}
}
В моем xaml я просто хочу связать это статическое свойство с текстовым полем:
<TextBox>
<TextBox.Text>
<Binding Source="{x:Static local:VersionManager.FilterString}"/>
</TextBox.Text>
</TextBox>
Все компилируется, но во время выполнения я получаю следующее исключение:
Невозможно преобразовать значение в атрибуте "Источник" в объект типа "System.Windows.Markup.StaticExtension". Ошибка в объекте "System.Windows.Data.Binding" в файле разметки "BurnDisk;component/selectversionpagefunction.xaml" Строка 57 Позиция 29.
Есть идеи, что я делаю не так?
13 ответов
Если привязка должна быть двусторонней, вы должны указать путь. Существует хитрость для двусторонней привязки статического свойства, при условии, что класс не является статическим: объявите фиктивный экземпляр класса в ресурсах и используйте его в качестве источника привязки.
<Window.Resources>
<local:VersionManager x:Key="versionManager"/>
</Window.Resources>
...
<TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>
Вы не можете привязать к такой статике. Инфраструктура привязки не может получать уведомления об обновлениях, поскольку DependencyObject
(или экземпляр объекта, который реализует INotifyPropertyChanged
) участвует.
Если это значение не меняется, просто отмените привязку и используйте x:Static
прямо внутри Text
имущество. определять app
ниже, чтобы быть местоположением пространства имен (и сборки) класса VersionManager.
<TextBox Text="{x:Static app:VersionManager.FilterString}" />
Если значение действительно меняется, я бы предложил создать синглтон, содержащий значение и привязанный к нему.
Пример синглтона:
public class VersionManager : DependencyObject {
public static readonly DependencyProperty FilterStringProperty =
DependencyProperty.Register( "FilterString", typeof( string ),
typeof( VersionManager ), new UIPropertyMetadata( "no version!" ) );
public string FilterString {
get { return (string) GetValue( FilterStringProperty ); }
set { SetValue( FilterStringProperty, value ); }
}
public static VersionManager Instance { get; private set; }
static VersionManager() {
Instance = new VersionManager();
}
}
<TextBox Text="{Binding Source={x:Static local:VersionManager.Instance},
Path=FilterString}"/>
В.NET 4.5 можно привязывать к статическим свойствам, подробнее
Вы можете использовать статические свойства в качестве источника привязки данных. Механизм привязки данных распознает, когда значение свойства изменяется, если возникает статическое событие. Например, если класс SomeClass определяет статическое свойство MyProperty, SomeClass может определить статическое событие, которое вызывается при изменении значения MyProperty. Статическое событие может использовать одну из следующих подписей:
public static event EventHandler MyPropertyChanged;
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
Обратите внимание, что в первом случае класс предоставляет статическое событие с именем PropertyNameChanged, которое передает EventArgs в обработчик событий. Во втором случае класс предоставляет статическое событие с именем StaticPropertyChanged, которое передает PropertyChangedEventArgs в обработчик событий. Класс, реализующий статическое свойство, может создавать уведомления об изменении свойства с использованием любого метода.
Начиная с WPF 4.5, вы можете связывать напрямую со статическими свойствами и автоматически обновлять привязку при изменении вашего свойства. Вам необходимо вручную подключить событие изменения, чтобы запустить обновления привязки.
public class VersionManager
{
private static String _filterString;
/// <summary>
/// A static property which you'd like to bind to
/// </summary>
public static String FilterString
{
get
{
return _filterString;
}
set
{
_filterString = value;
// Raise a change event
OnFilterStringChanged(EventArgs.Empty);
}
}
// Declare a static event representing changes to your static property
public static event EventHandler FilterStringChanged;
// Raise the change event through this static method
protected static void OnFilterStringChanged(EventArgs e)
{
EventHandler handler = FilterStringChanged;
if (handler != null)
{
handler(null, e);
}
}
static VersionManager()
{
// Set up an empty event handler
FilterStringChanged += (sender, e) => { return; };
}
}
Теперь вы можете связать свое статическое свойство, как и любое другое:
<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
Там может быть два способа / синтаксиса, чтобы связать static
имущество. Если р является static
собственность в классе MainWindow
, затем binding
за textbox
будет:
1.
<TextBox Text="{x:Static local:MainWindow.p}" />
2.
<TextBox Text="{Binding Source={x:Static local:MainWindow.p},Mode=OneTime}" />
Ты можешь использовать ObjectDataProvider
класс и это MethodName
имущество. Это может выглядеть так:
<Window.Resources>
<ObjectDataProvider x:Key="versionManager" ObjectType="{x:Type VersionManager}" MethodName="get_FilterString"></ObjectDataProvider>
</Window.Resources>
Заявленный объект данных провайдера можно использовать так:
<TextBox Text="{Binding Source={StaticResource versionManager}}" />
Если вы используете локальные ресурсы, вы можете ссылаться на них, как показано ниже:
<TextBlock Text="{Binding Source={x:Static prop:Resources.PerUnitOfMeasure}}" TextWrapping="Wrap" TextAlignment="Center"/>
Правильный вариант для.NET 4.5 +
Код C#
public class VersionManager
{
private static string filterString;
public static string FilterString
{
get => filterString;
set
{
if (filterString == value)
return;
filterString = value;
StaticPropertyChanged?.Invoke(null, FilterStringPropertyEventArgs);
}
}
private static readonly PropertyChangedEventArgs FilterStringPropertyEventArgs = new PropertyChangedEventArgs (nameof(FilterString));
public static event PropertyChangedEventHandler StaticPropertyChanged;
}
Привязка XAML (внимание к скобкам они (), а не {})
<TextBox Text="{Binding Path=(yournamespace:VersionManager.FilterString)}" />
Самый простой ответ (.net 4.5 и более поздние):
static public event EventHandler FilterStringChanged;
static string _filterString;
static public string FilterString
{
get { return _filterString; }
set
{
_filterString= value;
FilterStringChanged?.Invoke(null, EventArgs.Empty);
}
}
и XAML:
<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
Не пренебрегайте скобками
Посмотрите на мой проект CalcBinding, который предоставляет вам написание сложных выражений в значении свойства Path, включая статические свойства, свойства источника, Math и другие. Итак, вы можете написать это:
<TextBox Text="{c:Binding local:VersionManager.FilterString}"/>
Удачи!
Другое решение - создать обычный класс, который реализует PropertyChanger следующим образом
public class ViewProps : PropertyChanger
{
private string _MyValue = string.Empty;
public string MyValue
{
get {
return _MyValue
}
set
{
if (_MyValue == value)
{
return;
}
SetProperty(ref _MyValue, value);
}
}
}
Затем создайте статический экземпляр класса в том месте, где вы не будете
public class MyClass
{
private static ViewProps _ViewProps = null;
public static ViewProps ViewProps
{
get
{
if (_ViewProps == null)
{
_ViewProps = new ViewProps();
}
return _ViewProps;
}
}
}
А теперь используйте его как статическое свойство
<TextBlock Text="{x:Bind local:MyClass.ViewProps.MyValue, Mode=OneWay}" />
А вот и реализация PropertyChanger при необходимости
public abstract class PropertyChanger : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Предположим, у вас есть следующий класс:
public static class VersionManager
{
public static string FilterString;
}
Вы можете связать свою статическую переменную следующим образом:
<TextBox Text = {x:Static local:VersionManager.FilterString }/>
Все эти ответы хороши, если вы хотите следовать хорошим соглашениям, но OP хотел чего-то простого, чего я и хотел, вместо того, чтобы иметь дело с шаблонами проектирования GUI. Если все, что вам нужно сделать, это добавить строку в базовое приложение с графическим интерфейсом, вы можете обновить ad-hoc без каких-либо сложностей, вы можете просто получить к ней доступ прямо в исходном коде C#.
Допустим, у вас есть действительно простое приложение WPF MainWindow XAML, как это,
<Window x:Class="MyWPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyWPFApp"
mc:Ignorable="d"
Title="MainWindow"
Height="200"
Width="400"
Background="White" >
<Grid>
<TextBlock x:Name="textBlock"
Text=".."
HorizontalAlignment="Center"
VerticalAlignment="Top"
FontWeight="Bold"
FontFamily="Helvetica"
FontSize="16"
Foreground="Blue" Margin="0,10,0,0"
/>
<Button x:Name="Find_Kilroy"
Content="Poke Kilroy"
Click="Button_Click_Poke_Kilroy"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Helvetica"
FontWeight="Bold"
FontSize="14"
Width="280"
/>
</Grid>
</Window>
Это будет выглядеть примерно так:
В исходном коде вашего MainWindow XAML у вас может быть что-то вроде этого, где все, что мы делаем, изменяя значение напрямую через textBlock.Text
"s get
/ set
функциональность:
using System.Windows;
namespace MyWPFApp
{
public partial class MainWindow : Window
{
public MainWindow() { InitializeComponent(); }
private void Button_Click_Poke_Kilroy(object sender, RoutedEventArgs e)
{
textBlock.Text = " \\|||/\r\n" +
" (o o) \r\n" +
"----ooO- (_) -Ooo----";
}
}
}
Затем, когда вы запускаете это событие, нажав кнопку, вуаля! Килрой появляется:)