정적 속성에 바인딩


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.Textget/ 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.