Liên kết với tài sản tĩnh


168

Tôi đang gặp khó khăn khi liên kết một thuộc tính chuỗi tĩnh đơn giản với TextBox.

Đây là lớp có thuộc tính tĩnh:

public class VersionManager
{
    private static string filterString;

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

Trong xaml của tôi, tôi chỉ muốn liên kết thuộc tính tĩnh này với TextBox:

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

Mọi thứ biên dịch, nhưng trong thời gian chạy, tôi nhận được ngoại lệ sau:

Không thể chuyển đổi giá trị trong thuộc tính 'Nguồn' thành đối tượng của loại 'System.Windows.Markup.StaticExtension'. Lỗi tại đối tượng 'System.Windows.Data.Binding' trong tệp đánh dấu 'BurnDisk; thành phần / selectversionpagefeft.xaml' Dòng 57 Vị trí 29.

Bất cứ ý tưởng những gì tôi đang làm sai?

Câu trả lời:


168

Nếu ràng buộc cần phải là hai chiều, bạn phải cung cấp một đường dẫn. Có một mẹo để thực hiện liên kết hai chiều trên một thuộc tính tĩnh, với điều kiện lớp không tĩnh: khai báo một thể hiện giả của lớp trong tài nguyên và sử dụng nó làm nguồn của liên kết.

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

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

Câu trả lời này phù hợp hơn với trường hợp của tôi vì tôi không muốn giới thiệu DependencyObject cho lớp nguồn của mình. Cảm ơn vì tiền hỗ trợ!
Anthony Brien

6
Lưu ý rằng sẽ cho phép hộp văn bản của bạn đẩy giá trị trở lại vào thuộc tính tĩnh, nhưng sẽ không cập nhật hộp văn bản khi giá trị nguồn thay đổi.
Adam Sills

1
Điều đó tốt, tôi chỉ cần ràng buộc từ hộp văn bản đến Nguồn trong trường hợp này. Nếu tôi muốn liên kết hoạt động theo cách khác, tôi nhận thấy sự cần thiết của một trong các phương pháp sau: INotifyPropertyChanged, <PropertyName> Thay đổi thuộc tính hoặc thuộc tính phụ thuộc.
Anthony Brien

1
Lưu ý: Giải pháp này sẽ không hoạt động trong tình huống MVVM, vì bạn thường không có quyền truy cập vào các loại đối tượng bạn ràng buộc.
Rừng Antony

@thomas Tôi rất thích làm việc này cho tôi nhưng không thể. Tôi đã đăng vấn đề nan giải của mình như một câu hỏi khác ở đây: stackoverflow.com/questions/34656670/NH
Andrew Simpson

107

Bạn không thể liên kết với một tĩnh như thế. Không có cách nào để cơ sở hạ tầng ràng buộc nhận được thông báo về các bản cập nhật vì không có DependencyObject(hoặc đối tượng thực hiện INotifyPropertyChanged) liên quan.

Nếu giá trị đó không thay đổi, chỉ cần bỏ ràng buộc và sử dụng x:Statictrực tiếp bên trong Texttài sản. Xác định appbên dưới là vị trí không gian tên (và lắp ráp) của lớp VersionManager.

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

Nếu giá trị thay đổi, tôi khuyên bạn nên tạo một singleton để chứa giá trị và liên kết với giá trị đó.

Một ví dụ về 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}"/>

5
Có thật không? Tôi đã có thể thực hiện liên kết với Int32.MaxValue tĩnh rất giống với mẫu của tôi: <TextBox Text = {Binding Source = {x: Tĩnh sys: Int32.MaxValue}, Mode = OneWay} "/> Có phải đó là làm việc vì đó là một cách?
Anthony Brien

2
Vâng, bất kỳ ràng buộc hai chiều nào cũng yêu cầu giá trị thuộc tính Đường dẫn trên liên kết. Nguồn cần phải là một đối tượng có chứa thuộc tính được chỉ định bởi Đường dẫn. Chỉ định OneWay sẽ loại bỏ hạn chế đó.
Adam Sills

Ngoài ra, xin lỗi vì cập nhật muộn, nhưng tôi đã cập nhật câu trả lời ở trên với một mẫu.
Adam Sills

Có cách nào để liên kết một chuỗi tĩnh. Tôi có một mutibinding và một trong những đầu vào là một chuỗi cố định.
Nitin Chaudhari

39

Trong .NET 4.5 có thể liên kết với các thuộc tính tĩnh, đọc thêm

Bạn có thể sử dụng các thuộc tính tĩnh làm nguồn liên kết dữ liệu. Công cụ liên kết dữ liệu nhận ra khi giá trị của thuộc tính thay đổi nếu một sự kiện tĩnh được nêu ra. Ví dụ: nếu lớp someClass định nghĩa một thuộc tính tĩnh có tên MyProperty, thì một số lớp có thể định nghĩa một sự kiện tĩnh được đưa ra khi giá trị của MyProperty thay đổi. Sự kiện tĩnh có thể sử dụng một trong các chữ ký sau:

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

Lưu ý rằng trong trường hợp đầu tiên, lớp sẽ hiển thị một sự kiện tĩnh có tên là PropertyNameChanged chuyển EventArss cho trình xử lý sự kiện. Trong trường hợp thứ hai, lớp này hiển thị một sự kiện tĩnh có tên là StaticPropertyChanged, chuyển thuộc tính PropertyChangedEventArss cho bộ xử lý sự kiện. Một lớp thực hiện thuộc tính tĩnh có thể chọn để đưa ra các thông báo thay đổi thuộc tính bằng một trong hai phương thức.


Đây là liên kết trong trường hợp bất cứ ai muốn đọc thêm. Microsoft đã gỡ nó xuống, nhưng nó đã được lưu trữ trên web ở đây. web.archive.org/web/20131129053934/http://msdn.microsoft.com/...
C. Tewalt

Câu trả lời này đã chỉ cho tôi đi đúng hướng, nhưng vẫn mất một thời gian để tìm ra các chi tiết mà không có ví dụ. Tôi đã viết một ví dụ dựa trên mã gốc.
Matt

13

Kể từ WPF 4.5, bạn có thể liên kết trực tiếp với các thuộc tính tĩnh và có ràng buộc tự động cập nhật khi thuộc tính của bạn được thay đổi. Bạn cần phải tự kết nối một sự kiện thay đổi để kích hoạt các cập nhật ràng buộc.

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

}

Bây giờ bạn có thể liên kết tài sản tĩnh của mình giống như bất kỳ tài sản tĩnh nào khác:

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

1
Các VersionManagerlớp có thể tĩnh và mọi thứ vẫn hoạt động. Lưu ý các dấu ngoặc trong định nghĩa đường dẫn Path=(local:VersionManager.FilterString). Có ai biết tại sao họ thực sự cần thiết?
chviLadislav

2
Các dấu ngoặc trong định nghĩa đường dẫn là cần thiết vì thuộc tính là tĩnh, xem tại đây
chviLadislav

11

Có thể có hai cách / cú pháp để ràng buộc một statictài sản. Nếu p là một staticthuộc tính trong lớp MainWindow, thì bindingfor textboxsẽ là:

1.

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

2.

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

9

Bạn có thể sử dụng ObjectDataProviderlớp và đó là MethodNametài sản. Nó có thể trông như thế này:

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

Nhà cung cấp dữ liệu đối tượng được khai báo có thể được sử dụng như thế này:

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

8

Nếu bạn đang sử dụng tài nguyên địa phương, bạn có thể tham khảo chúng như dưới đây:

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

3

Biến thể bên phải cho .NET 4.5 +

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

Liên kết XAML (chú ý đến dấu ngoặc nhọn (), không phải {})

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

Thực hiện một thay đổi nhỏ cho mã của bạn để gọi EventHandler đúng cách.
Đánh dấu A. Donohoe

Đã thử rất nhiều giải pháp khác nhau và giải pháp này đã có hiệu quả. PropertyChangedEventHandler là những gì làm việc cho tôi. Chúc mừng.
Mgamerz

2

Nhìn vào dự án CalcBinding của tôi , nó cung cấp cho bạn viết các biểu thức phức tạp trong giá trị thuộc tính Đường dẫn, bao gồm các thuộc tính tĩnh, thuộc tính nguồn, Toán học và khác. Vì vậy, bạn có thể viết điều này:

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

Chúc may mắn!


0

Những câu trả lời này đều tốt nếu bạn muốn tuân theo các quy ước tốt nhưng OP muốn một cái gì đó đơn giản , đó là điều tôi cũng muốn thay vì xử lý các mẫu thiết kế GUI. Nếu tất cả những gì bạn muốn làm là có một chuỗi trong ứng dụng GUI cơ bản, bạn có thể cập nhật đặc biệt mà không cần bất cứ điều gì lạ mắt, bạn chỉ có thể truy cập trực tiếp vào nguồn C # của mình.

Giả sử bạn đã có một ứng dụng WPF thực sự cơ bản MainWindow XAML như thế này,

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

Điều đó sẽ trông giống như thế này:

nhập mô tả hình ảnh ở đây

Trong nguồn MainWindow XAML của bạn, bạn có thể có một cái gì đó giống như thế này, nơi tất cả chúng ta đang làm để thay đổi giá trị trực tiếp thông qua textBlock.Text's get/ setfunction:

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

Sau đó, khi bạn kích hoạt sự kiện nhấp đó bằng cách nhấp vào nút, thì đấy! Kilroy xuất hiện :)

nhập mô tả hình ảnh ở đây


0

Một giải pháp khác là tạo một lớp bình thường thực hiện PropertyChanger như thế này

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

Sau đó tạo một thể hiện tĩnh của lớp ở đâu đó bạn sẽ không

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

Và bây giờ sử dụng nó như là tài sản tĩnh

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

Và đây là triển khai PropertyChanger nếu cần thiết

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

Câu trả lời nhẹ nhất (.net 4.5 trở lên):

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

và XAML:

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

Đừng bỏ qua dấu ngoặc

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.