Statik özelliğe bağlanma


169

Bir TextBox basit bir statik dize özelliği bağlama zor bir zaman yaşıyorum.

Static özelliğine sahip sınıf:

public class VersionManager
{
    private static string filterString;

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

Benim xaml, ben sadece bu statik özelliği bir TextBox bağlamak istiyorum:

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

Her şey derleniyor, ancak çalışma zamanında aşağıdaki istisnayı alıyorum:

'Kaynak' özelliğindeki değer 'System.Windows.Markup.StaticExtension' türündeki nesneye dönüştürülemiyor. 'BurnDisk; bileşen / selectversionpagefunction.xaml' biçimlendirme dosyasındaki 'System.Windows.Data.Binding' nesnesinde hata. Satır 57 Konum 29.

Ne yaptığımı biliyor musun?

Yanıtlar:


170

Bağlamanın iki yönlü olması gerekiyorsa, bir yol sağlamanız gerekir.

Sınıfın statik olmaması şartıyla, statik bir özellik üzerinde iki yönlü bağlanma yapmak için bir hile vardır: kaynaklarda sınıfın kukla bir örneğini bildirin ve bağlamanın kaynağı olarak kullanın.

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

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

Kaynak dersime DependencyObject'i tanıtmak istemediğim için bu yanıt benim durumum için daha uygun. Bahşiş için teşekkürler!
Anthony Brien

6
Metin kutunuzun değeri tekrar statik özelliğe itmesini sağlayacak, ancak kaynak değeri değiştiğinde metin kutusunu güncelleştirmeyeceğinizi unutmayın.
Adam

1
Bu iyi, bu durumda metin kutusundan Kaynak'a bağlanmaya ihtiyacım vardı. Bağlamanın başka bir şekilde çalışmasını istiyorsanız, şu yöntemlerden birine ihtiyacım olduğunu biliyorum: INotifyPropertyChanged, <PropertyName> Değişen olay veya bağımlılık özelliği.
Anthony Brien

1
Not: Bu çözüm bir MVVM durumunda çalışmaz, çünkü genellikle bağlandığınız nesne türlerine erişemezsiniz.
Antony Woods

@homas bunu benim için işe almak isterdim ama yapamam. İkilemi burada başka bir soru olarak yayınladım: stackoverflow.com/questions/34656670/…
Andrew Simpson

108

Böyle bir statik elektriğe bağlanamazsınız. Bağlayıcı altyapının güncellemelerden haberdar edilmesinin bir yolu yoktur, çünkü ilgili DependencyObject(veya uygulayan nesne örneği INotifyPropertyChanged) yoktur.

Bu değer değişmezse, yalnızca bağlamayı kaldırın ve x:Staticdoğrudan Textözelliğin içinde kullanın . Define appVersionManager sınıfının ad (ve montaj) konum olmak üzere aşağıdaki.

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

Değer değişirse, değeri içermek ve buna bağlamak için bir singleton oluşturmayı öneririm.

Singleton'a bir örnek:

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
Gerçekten mi? Benim örneğe çok benzer statik Int32.MaxValue bağlayabildim: <TextBox Text = {Binding Source = {x: Statik sys: Int32.MaxValue}, Mode = OneWay} "/> tek yolu olduğu için mi çalışıyorsunuz?
Anthony Brien

2
Evet, herhangi bir iki yönlü bağlanma, bağlayıcı üzerinde bir Path özellik değeri gerektirir. Kaynak, Yol tarafından belirtilen özelliği içeren bir nesne olmalıdır. OneWay belirtildiğinde bu kısıtlama kaldırılır.
Adam

Ayrıca, geç güncelleme için üzgünüm, ancak yukarıdaki cevabı bir örnekle güncelledim.
Adam

Statik bir dize bağlamanın bir yolu var mı? Ben bir mutibinding ve giriş biri sabit dize.
Nitin Chaudhari

39

.NET 4.5'te statik özelliklere bağlanmak mümkündür, daha fazla bilgi edinin

Veri bağlamanın kaynağı olarak statik özellikleri kullanabilirsiniz. Veri bağlama motoru, statik bir olay ortaya çıkarsa özelliğin değerinin ne zaman değiştiğini tanır. Örneğin, SomeClass sınıfı MyProperty adlı statik bir özellik tanımlarsa, SomeClass MyProperty değeri değiştiğinde yükseltilen statik bir olay tanımlayabilir. Statik olay aşağıdaki imzalardan birini kullanabilir:

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

İlk durumda, sınıfın EventArgs öğesini olay işleyicisine ileten PropertyNameChanged adlı statik bir olayı ortaya çıkardığını unutmayın. İkinci durumda, sınıf, olay işleyicisine PropertyChangedEventArgs ileten StaticPropertyChanged adlı statik bir olay gösterir. Statik özelliği uygulayan bir sınıf, iki yöntemden birini kullanarak özellik değişikliği bildirimlerini yükseltmeyi seçebilir.


Herkesin daha fazla okumak istemesi durumunda bağlantı. Microsoft indirdi, ama burada web arşivinde. web.archive.org/web/20131129053934/http://msdn.microsoft.com/…
C. Tewalt

Bu cevap beni doğru yöne çekti, ancak detayları örnek olmadan çözmek biraz zaman aldı. Orijinal koda dayalı bir örnek yazdım .
Matt

13

WPF 4.5'den itibaren, statik özelliklere doğrudan bağlanabilir ve mülkünüz değiştiğinde bağlamanın otomatik olarak güncellenmesini sağlayabilirsiniz. Bağlama güncelleştirmelerini tetiklemek için bir değişiklik olayını el ile bağlamanız gerekir.

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

}

Artık statik mülkünüzü tıpkı diğerleri gibi bağlayabilirsiniz:

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

1
VersionManagerSınıf statik olabilir ve her şey hala çalışıyor. Yol tanımındaki parantezlere dikkat edin Path=(local:VersionManager.FilterString). Neden gerçekten ihtiyaç duyulduğunu bilen var mı?
chviLadislav

2
Özellik statik olduğu için yol tanımındaki parantezlere ihtiyaç vardır, buraya
chviLadislav

11

Bir staticözelliği bağlamak için iki yol / sözdizimi olabilir . Eğer p bir olduğunu staticsınıfta mülkiyet MainWindow, daha sonra bindingiçin textboxolacaktır:

1.

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

2.

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

9

Sen kullanabilirsiniz ObjectDataProvidersınıf ve bu kadar MethodNameözellik. Şöyle görünebilir:

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

Bildirilen nesne veri sağlayıcısı şu şekilde kullanılabilir:

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

8

Yerel kaynakları kullanıyorsanız bunlara aşağıdaki gibi başvurabilirsiniz:

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

3

.NET 4.5 + için doğru varyant

C # kodu

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 bağlaması (parantezlere dikkat (), {} değil)

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

EventHandler'ı doğru şekilde çağırmak için kodunuzda küçük bir değişiklik yaptı.
Mark A. Donohoe

Birçok farklı çözüm denedim ve bu işe yaradı. Benim için işe yarayan PropertyChangedEventHandler. Şerefe.
Mgamerz

2

Statik özellikler, kaynak özellikleri, Math ve diğerleri de dahil olmak üzere Path özellik değerinde karmaşık ifadeler yazmanızı sağlayan CalcBinding projeme bakın . Yani, şunu yazabilirsiniz:

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

İyi şanslar!


0

İyi cevapları takip etmek istiyorsanız bu cevapların hepsi iyi ama OP basit bir şey istedi , bu da GUI tasarım desenleriyle uğraşmak yerine istediğim şeydi . Yapmak istediğiniz tek şey temel bir GUI uygulamasında bir dize varsa, geçici bir şey süslemeden güncelleyebilirsiniz, doğrudan C # kaynağınızdan erişebilirsiniz.

Diyelim ki böyle basit bir WPF uygulaması MainWindow XAML var,

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

Bu şöyle görünecektir:

resim açıklamasını buraya girin

MainWindow XAML kaynağınızda, değeri doğrudan textBlock.Text's get/ setişlevsellik yoluyla değiştirmek için yaptığımız her şeye sahip olabilirsiniz :

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

Ardından, bu tıklama etkinliğini düğmeye tıklayarak tetiklediğinizde, voila! Kilroy ortaya çıkar :)

resim açıklamasını buraya girin


0

Başka bir çözüm, PropertyChanger'ı bu şekilde uygulayan normal bir sınıf oluşturmaktır

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

Sonra alışılmadık bir yerde sınıfın statik bir örneğini oluşturun

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

Ve şimdi statik özellik olarak kullanın

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

Ve burada gerekirse PropertyChanger uygulaması

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

Yalın cevap (.net 4.5 ve üstü):

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

ve XAML:

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

Parantezleri ihmal etmeyin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.