Прив’язка до статичної властивості


168

Мені важко пов’язати просту властивість статичного рядка з TextBox.

Ось клас зі статичною властивістю:

public class VersionManager
{
    private static string filterString;

    public static string FilterString
    {
        get { return filterString; }
        set { filterString = value; }
    }
}

У своєму xaml я просто хочу прив’язати цю статичну властивість до TextBox:

<TextBox>
    <TextBox.Text>
        <Binding Source="{x:Static local:VersionManager.FilterString}"/>
    </TextBox.Text>
</TextBox>

Все компілюється, але під час виконання, я отримую таке виняток:

Неможливо перетворити значення в атрибут "Джерело" в об'єкт типу "System.Windows.Markup.StaticExtension". Помилка в об'єкті "System.Windows.Data.Binding" у файлі розмітки "BurnDisk; компонент / selectversionpagefunction.xaml" Рядок 57 Позиція 29.

Будь-яка ідея, що я роблю неправильно?

Відповіді:


168

Якщо прив'язка повинна бути двосторонньою, вам слід надати шлях. Існує хитрість зробити двостороння прив'язка статичної властивості, за умови, що клас не є статичним: оголосити фіктивний екземпляр класу в ресурсах і використовувати його як джерело прив'язки.

<Window.Resources>
    <local:VersionManager x:Key="versionManager"/>
</Window.Resources>
...

<TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>

Ця відповідь більше відповідає моєму випадку, оскільки я не хочу вводити DependencyObject до свого джерельного класу. Дякую за пораду!
Ентоні Бріен

6
Зверніть увагу, що дозволить вашому текстовому полі повернути значення назад у статичну властивість, але не оновить текстове поле, коли значення джерела зміниться.
Адам Сілз

1
Це добре, я просто потребував прив’язки з текстового поля до Джерела в цьому випадку. Якщо я хочу, щоб прив'язка працювала іншим способом, я усвідомлюю необхідність одного з таких методів: INotifyPropertyChanged, <PropertyName> Змінено властивість події або залежності.
Ентоні Бріен

1
Примітка. Це рішення не працюватиме в ситуації MVVM, оскільки ви, як правило, не маєте доступу до типів об'єктів, до яких ви зобов’язуєтесь.
Ентоні Вудс

@thomas Я хотів би, щоб це працювало на мене, але не можу. Я розмістив свою дилему як ще одне питання тут: stackoverflow.com/questions/34656670/…
Ендрю Сімпсон

107

Ви не можете прив'язатись до такої статики. Немає можливості для інфраструктури прив'язки отримувати сповіщення про оновлення, оскільки тут не задіяно 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}"/>

5
Дійсно? Мені вдалося прив’язати до статичного Int32.MaxValue, який дуже схожий на мій зразок: <TextBox Text = {Прив'язуючий джерело = {x: Статичний sys: Int32.MaxValue}, Mode = OneWay} "/> Це робота, тому що це один спосіб?
Ентоні Бріен

2
Так, будь-яка двостороння прив'язка вимагає значення властивості Path на прив'язку. Джерелом повинен бути об'єкт, який містить властивість, вказану Path. Якщо вказати OneWay, це обмеження видаляється.
Адам Підвіконня

Також вибачте за пізнє оновлення, але я оновив вищевказану відповідь зразком.
Адам Сілз

Чи є спосіб зв’язати статичну рядок. У мене є вимкнення, і один із вхідних даних - це фіксований рядок.
Нітін Чаудхарі

39

У .NET 4.5 можна прив’язатись до статичних властивостей, читайте докладніше

Ви можете використовувати статичні властивості як джерело прив'язки даних. Двигун прив'язки даних розпізнає, коли значення властивості змінюється, якщо статична подія піднімається. Наприклад, якщо клас SomeClass визначає статичну властивість під назвою MyProperty, SomeClass може визначити статичну подію, яка піднімається при зміні значення MyProperty. Статична подія може використовувати будь-який з наступних підписів:

public static event EventHandler MyPropertyChanged; 
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged; 

Зауважте, що в першому випадку клас виставляє статичну подію на ім'я PropertyNameChanged, яка передає EventArgs обробнику подій. У другому випадку клас виставляє статичну подію на ім'я StaticPropertyChanged, яка передає PropertyChangedEventArgs до обробника подій. Клас, який реалізує статичну властивість, може обрати сповіщення про зміну властивості за допомогою будь-якого методу.


Ось посилання на випадок, якщо хтось хоче прочитати більше. Microsoft зняла це, але тут це веб-архів. web.archive.org/web/20131129053934/http://msdn.microsoft.com/…
C. Tewalt

Ця відповідь спрямовувала мене в правильному напрямку, але все ж знадобився певний час, щоб детально опрацювати без прикладу. Я написав приклад на основі оригінального коду.
Метт

13

Станом на 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)}"/>

1
VersionManagerКлас може бути статичним , і все ще працює. Зверніть увагу на дужки у визначенні шляху Path=(local:VersionManager.FilterString). Хтось знає, навіщо вони насправді потрібні?
chviLadislav

2
Дужки в визначенні шляху потрібні, оскільки властивість статична, дивіться тут
chviLadislav

11

Існує два способи / синтаксис для прив’язки staticвластивості. Якщо p - staticвластивість у класі MainWindow, то bindingдля textboxбуде:

1.

<TextBox Text="{x:Static local:MainWindow.p}" />

2.

<TextBox Text="{Binding Source={x:Static local:MainWindow.p},Mode=OneTime}" />

9

Ви можете використовувати ObjectDataProviderклас та його MethodNameвластивість. Це може виглядати так:

<Window.Resources>
   <ObjectDataProvider x:Key="versionManager" ObjectType="{x:Type VersionManager}" MethodName="get_FilterString"></ObjectDataProvider>
</Window.Resources>

Оголошений постачальник даних об’єктів може використовуватися так:

<TextBox Text="{Binding Source={StaticResource versionManager}}" />

8

Якщо ви використовуєте місцеві ресурси, ви можете посилатися на них, як показано нижче:

<TextBlock Text="{Binding Source={x:Static prop:Resources.PerUnitOfMeasure}}" TextWrapping="Wrap" TextAlignment="Center"/>

3

Правильний варіант для .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)}" />

Внесло невелику зміну в код, щоб правильно викликати EventHandler.
Марк А. Донохое

Спробував багато різних рішень, і це спрацювало. PropertyChangedEventHandler - це те, що працювало для мене. Ура.
Mgamerz

2

Подивіться на мій проект CalcBinding , який надає вам записати складні вирази у значенні властивості Path, включаючи статичні властивості, властивості джерела, Math та інші. Отже, ви можете написати це:

<TextBox Text="{c:Binding local:VersionManager.FilterString}"/>

Удачі!


0

Ці відповіді є гарними, якщо ви хочете дотримуватися добрих конвенцій, але ОП хотів чогось простого , чого я хотів і замість того, щоб мати справу з моделями дизайну GUI. Якщо все, що ви хочете зробити, - це мати рядок у базовій програмі 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>

Це буде виглядати приблизно так:

введіть тут опис зображення

У джерелі XAML MainWindow у вас може виникнути щось подібне, де все, що ми робимо, безпосередньо змінюючи значення через 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----";
        }
    }
}

Потім, коли ви запускаєте цю подію натисканням кнопки, вуаля! З'являється Кілрой :)

введіть тут опис зображення


0

Іншим рішенням є створення нормального класу, який реалізує подібний 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));
    }
}

-1

Найменша відповідь (.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)}"/>

Не нехтуйте дужками

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.