정적 속성에 바인딩


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>

모든 것이 컴파일되지만 런타임에 다음 예외가 발생합니다.

'Source'특성의 값을 'System.Windows.Markup.StaticExtension'유형의 개체로 변환 할 수 없습니다. 마크 업 파일 'BurnDisk; component / selectversionpagefunction.xaml'의 'System.Windows.Data.Binding'개체에서 오류 57 행 29 위치.

내가 뭘 잘못하고 있는지 알아?

답변:


168

바인딩이 양방향이어야하는 경우 경로를 제공해야합니다. 클래스가 정적이 아닌 경우 정적 속성에 양방향 바인딩을 수행하는 트릭이 있습니다. 리소스에서 클래스의 더미 인스턴스를 선언하고 바인딩의 소스로 사용하십시오.

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

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

이 대답은 소스 클래스에 DependencyObject를 도입하고 싶지 않기 때문에 내 경우에 더 적합합니다. 팁 고마워!
— Anthony Brien 2016 년

6
텍스트 상자에서 값을 정적 속성으로 다시 푸시 할 수 있지만 소스 값이 변경 될 때 텍스트 상자를 업데이트하지 않습니다.
— Adam Sills

1
괜찮습니다.이 경우 텍스트 상자에서 소스로의 바인딩이 필요했습니다. 바인딩이 다른 방식으로 작동하도록하려면 INotifyPropertyChanged, <PropertyName> 변경된 이벤트 또는 종속성 속성 중 하나가 필요하다는 것을 알고 있습니다.
— Anthony Brien 2016 년

1
참고 :이 솔루션은 일반적으로 바인딩하려는 객체 유형에 액세스 할 수 없으므로 MVVM 상황에서는 작동하지 않습니다.
— Antony Woods

@ 토마스 나는 이것을 작동시키기를 원하지만 할 수는 없습니다. 여기 또 다른 질문으로 내 딜레마를 게시 : stackoverflow.com/questions/34656670/...
— 앤드류 심슨

107

그런 정적에는 바인딩 할 수 없습니다. DependencyObject(또는 구현하는 객체 인스턴스)가 없기 때문에 바인딩 인프라가 업데이트에 대한 알림을받을 방법이 없습니다 INotifyPropertyChanged.

해당 값이 변경되지 않으면 바인딩을 버리고 속성 x:Static내에서 직접 사용 하십시오 Text. appVersionManager 클래스의 네임 스페이스 (및 어셈블리) 위치로 아래를 정의하십시오 .

<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 = {Binding Source = {x : Static sys : Int32.MaxValue}, Mode = OneWay} "/> 그것은 하나의 방법이기 때문에 작업?
— 안토니 Brien의

2
예, 양방향 바인딩에는 바인딩에 대한 Path 속성 값이 필요합니다. Source는 Path에 의해 지정된 속성을 포함하는 객체 여야합니다. OneWay를 지정하면 해당 제한이 제거됩니다.
— Adam Sills

또한 최신 업데이트에 대해 죄송하지만 위의 답변을 샘플로 업데이트했습니다.
— Adam Sills

정적 문자열을 바인딩하는 방법이 있습니까? 나는 mutibinding을 가지고 있고 입력 중 하나는 고정 문자열입니다.
— Nitin Chaudhari

39

.NET 4.5에서는 정적 속성에 바인딩 할 수 있습니다.

정적 속성을 데이터 바인딩 소스로 사용할 수 있습니다. 정적 이벤트가 발생하면 데이터 바인딩 엔진이 특성 값이 변경되는시기를 인식합니다. 예를 들어 SomeClass 클래스가 MyProperty라는 정적 속성을 정의하면 SomeClass는 MyProperty 값이 변경 될 때 발생하는 정적 이벤트를 정의 할 수 있습니다. 정적 이벤트는 다음 서명 중 하나를 사용할 수 있습니다.

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

첫 번째 경우 클래스는 EventArgs를 이벤트 핸들러로 전달하는 PropertyNameChanged라는 정적 이벤트를 노출합니다. 두 번째 경우, 클래스는 PropertyChangedEventArgs를 이벤트 핸들러로 전달하는 StaticPropertyChanged라는 정적 이벤트를 노출합니다. 정적 속성을 구현하는 클래스는 두 방법 중 하나를 사용하여 속성 변경 알림을 발생시킬 수 있습니다.


누구나 더 많은 것을 읽고 싶어하는 경우에 대한 링크는 다음과 같습니다. Microsoft는이를 중단했지만 여기 웹 아카이브에 있습니다. web.archive.org/web/20131129053934/http://msdn.microsoft.com/...
— C. Tewalt

이 대답은 올바른 방향으로 나를 가리 켰지 만 예제없이 세부 사항을 해결하는 데 여전히 시간이 걸렸습니다. 원래 코드 를 기반으로 예제 를 작성했습니다 .
— Matt

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는 A는 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를 올바르게 호출하기 위해 코드를 약간 변경했습니다.
— Mark A. Donohoe

많은 다른 솔루션을 시험해 보았습니다. PropertyChangedEventHandler가 나를 위해 일한 것입니다. 건배.
— Mgamerz

2

정적 속성, 소스 속성, Math 및 기타를 포함하여 Path 속성 값으로 복잡한 표현식을 작성하는 데 사용할 수있는 내 프로젝트 CalcBinding을 살펴보십시오 . 따라서 다음과 같이 작성할 수 있습니다.

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

행운을 빕니다!


0

이 답변은 좋은 규칙을 따르고 싶지만 OP는 간단 하지만 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>

다음과 같이 보일 것입니다.

여기에 이미지 설명을 입력하십시오

MainWindow XAML의 소스에서 textBlock.Text의 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)}"/>

괄호를 무시하지 마십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.