Mengikat ke properti statis

168

Saya mengalami kesulitan mengikat properti string statis sederhana ke TextBox.

Inilah kelas dengan properti statis:

public class VersionManager
{
    private static string filterString;

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

Di xaml saya, saya hanya ingin mengikat properti statis ini ke TextBox:

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

Semuanya dikompilasi, tetapi saat dijalankan, saya mendapatkan pengecualian berikut:

Tidak dapat mengonversi nilai dalam atribut 'Sumber' ke objek jenis 'System.Windows.Markup.StaticExtension'. Kesalahan pada objek 'System.Windows.Data.Binding' dalam file markup 'BurnDisk; komponen / selectversionpagefunction.xaml' Baris 57 Posisi 29.

Ada yang tahu apa yang saya lakukan salah?

Anthony Brien
sumber

Jawaban:

168

Jika pengikatan harus dua arah, Anda harus menyediakan jalur. Ada trik untuk melakukan pengikatan dua arah pada properti statis, asalkan kelasnya tidak statis: nyatakan instance dummy kelas dalam sumber daya, dan gunakan itu sebagai sumber pengikatan.

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

<TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>
Thomas Levesque
sumber
Jawaban ini lebih sesuai untuk kasus saya karena saya tidak ingin memperkenalkan DependencyObject ke kelas sumber saya. Terima kasih atas tipnya!
Anthony Brien
6
Catatan yang akan memungkinkan kotak teks Anda untuk mendorong nilai kembali ke properti statis, tetapi tidak akan memperbarui kotak teks ketika nilai sumber berubah.
Adam Sills
1
Tidak apa-apa, saya hanya perlu pengikatan dari kotak teks ke Sumber dalam kasus ini. Jika saya ingin pengikatan bekerja dengan cara lain, saya menyadari perlunya salah satu metode ini: INotifyPropertyChanged, <PropertyName> Berubah properti acara atau ketergantungan.
Anthony Brien
1
Catatan: Solusi ini tidak akan berfungsi dalam situasi MVVM, karena Anda biasanya tidak memiliki akses ke jenis objek yang Anda ikat.
Antony Woods
@ Thomas Saya ingin ini bekerja untuk saya tetapi tidak bisa. Saya memposting dilema saya sebagai pertanyaan lain di sini: stackoverflow.com/questions/34656670/…
Andrew Simpson
107

Anda tidak dapat mengikat ke statis seperti itu. Tidak ada cara untuk mengikat infrastruktur untuk mendapatkan pemberitahuan tentang pembaruan karena tidak ada DependencyObject(atau objek contoh yang mengimplementasikan INotifyPropertyChanged) yang terlibat.

Jika nilai itu tidak berubah, cukup lepaskan ikatan dan gunakan x:Staticlangsung di dalam Textproperti. Tetapkan di appbawah ini untuk menjadi lokasi namespace (dan perakitan) dari kelas VersionManager.

<TextBox Text="{x:Static app:VersionManager.FilterString}" />

Jika nilainya berubah, saya sarankan membuat singleton untuk memuat nilainya dan mengikatnya.

Contoh dari singleton:

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}"/>
Adam Sills
sumber
5
Betulkah? Saya sudah bisa melakukan binding ke Int32.MaxValue statis yang sangat mirip dengan sampel saya: <TextBox Text = {Binding Source = {x: Static sys: Int32.MaxValue}, Mode = OneWay} "/> Apakah itu bekerja karena itu satu arah?
Anthony Brien
2
Ya, pengikatan dua arah membutuhkan nilai properti Path pada pengikatan. Sumber harus berupa objek yang berisi properti yang ditentukan oleh Path. Menentukan OneWay menghapus batasan itu.
Adam Sills
Juga, maaf atas keterlambatan pembaruan, tetapi saya memperbarui jawaban di atas dengan sampel.
Adam Sills
Apakah ada cara untuk mengikat string statis. Saya punya mutibinding dan salah satu input adalah string tetap.
Nitin Chaudhari
39

Di .NET 4.5 dimungkinkan untuk mengikat ke properti statis, baca lebih lanjut

Anda dapat menggunakan properti statis sebagai sumber pengikatan data. Mesin pengikat data mengenali kapan nilai properti berubah jika acara statis dinaikkan. Misalnya, jika kelas SomeClass mendefinisikan properti statis yang disebut MyProperty, SomeClass dapat mendefinisikan acara statis yang dinaikkan ketika nilai perubahan MyProperty. Acara statis dapat menggunakan salah satu dari tanda tangan berikut:

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

Perhatikan bahwa dalam kasus pertama, kelas mengekspos peristiwa statis bernama PropertyNameChanged yang meneruskan EventArgs ke pengendali event. Dalam kasus kedua, kelas mengekspos peristiwa statis bernama StaticPropertyChanged yang melewati PropertyChangedEventArgs ke pengendali acara. Kelas yang mengimplementasikan properti statis dapat memilih untuk meningkatkan pemberitahuan perubahan properti menggunakan metode mana pun.

Jowen
sumber
Berikut tautan jika ada yang ingin membaca lebih lanjut. Microsoft mengambilnya, tetapi ada di arsip web di sini. web.archive.org/web/20131129053934/http://msdn.microsoft.com/…
C. Tewalt
Jawaban ini menunjuk saya ke arah yang benar, tetapi masih butuh waktu untuk mengerjakan detailnya tanpa contoh. Saya telah menulis contoh berdasarkan kode asli.
Matt
13

Pada WPF 4.5 Anda dapat mengikat langsung ke properti statis dan mengikat secara otomatis memperbarui ketika properti Anda diubah. Anda perlu memasang perubahan secara manual untuk memicu pembaruan yang mengikat.

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; };
    }

}

Sekarang Anda dapat mengikat properti statis Anda seperti yang lain:

<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
Mat
sumber
1
The VersionManagerkelas dapat statis dan semuanya masih bekerja. Perhatikan kurung kurawal dalam definisi path Path=(local:VersionManager.FilterString). Adakah yang tahu mengapa mereka sebenarnya dibutuhkan?
chviLadislav
2
Kawat gigi dalam definisi jalur diperlukan karena propertinya statis, lihat di sini
chviLadislav
11

Mungkin ada dua cara / sintaks untuk mengikat staticproperti. Jika p adalah staticproperti di kelas MainWindow, maka bindinguntuk textboxakan:

1.

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

2.

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

Anda dapat menggunakan ObjectDataProviderkelas dan MethodNameproperti itu. Ini bisa terlihat seperti ini:

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

Penyedia data objek yang dinyatakan dapat digunakan seperti ini:

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

Jika Anda menggunakan sumber daya lokal Anda dapat merujuk mereka sebagai berikut:

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

Varian yang tepat untuk .NET 4.5 +

Kode 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;
}

Ikatan XAML (perhatian pada kawat gigi mereka (), bukan {})

<TextBox Text="{Binding Path=(yournamespace:VersionManager.FilterString)}" />
Alexei Shcherbakov
sumber
Buat sedikit perubahan pada kode Anda untuk memanggil EventHandler dengan benar.
Mark A. Donohoe
Mencoba banyak solusi berbeda dan ini berhasil. PropertyChangedEventHandler adalah yang bekerja untuk saya. Bersulang.
Mgamerz
2

Lihatlah proyek saya CalcBinding , yang memberi Anda menulis ekspresi kompleks dalam nilai properti Path, termasuk properti statis, properti sumber, Matematika, dan lainnya. Jadi, Anda bisa menulis ini:

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

Semoga berhasil!

Alex141
sumber
0

Semua jawaban ini bagus jika Anda ingin mengikuti konvensi yang baik tetapi OP menginginkan sesuatu yang sederhana , yang saya inginkan juga daripada berurusan dengan pola desain GUI. Jika semua yang ingin Anda lakukan adalah memiliki string dalam aplikasi GUI dasar yang dapat Anda perbarui ad-hoc tanpa sesuatu yang mewah, Anda bisa mengaksesnya langsung di sumber C # Anda.

Katakanlah Anda punya aplikasi WPF yang sangat mendasar MainWindow XAML seperti ini,

<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>

Itu akan terlihat seperti ini:

masukkan deskripsi gambar di sini

Dalam sumber MainWindow XAML ini, Anda bisa memiliki sesuatu seperti ini di mana semua yang kita lakukan dalam mengubah nilai langsung melalui textBlock.Text's get/ setfungsi:

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----";
        }
    }
}

Lalu ketika Anda memicu acara klik itu dengan mengklik tombol, voila! Kilroy muncul :)

masukkan deskripsi gambar di sini

kayleeFrye_onDeck
sumber
0

Solusi lain adalah membuat kelas normal yang mengimplementasikan PropertyChanger seperti ini

public class ViewProps : PropertyChanger
{
    private string _MyValue = string.Empty;
    public string MyValue
    {
        get { 
            return _MyValue
        }
        set
        {
            if (_MyValue == value)
            {
                return;
            }
            SetProperty(ref _MyValue, value);
        }
    }
}

Kemudian buat instance statis dari kelas di suatu tempat yang tidak Anda inginkan

public class MyClass
{
    private static ViewProps _ViewProps = null;
    public static ViewProps ViewProps
    {
        get
        {
            if (_ViewProps == null)
            {
                _ViewProps = new ViewProps();
            }
            return _ViewProps;
        }
    }
}

Dan sekarang menggunakannya sebagai properti statis

<TextBlock  Text="{x:Bind local:MyClass.ViewProps.MyValue, Mode=OneWay}"  />

Dan di sini adalah implementasi PropertyChanger jika perlu

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));
    }
}
neosonne
sumber
-1

Jawaban teranyar (.net 4.5 dan yang lebih baru):

    static public event EventHandler FilterStringChanged;
    static string _filterString;
    static public string FilterString
    {
        get { return _filterString; }
        set
        {
            _filterString= value;
            FilterStringChanged?.Invoke(null, EventArgs.Empty);
        }
    }

dan XAML:

    <TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>

Jangan abaikan tanda kurung

Sean
sumber