ผูกพันกับคุณสมบัติคงที่


168

ฉันมีช่วงเวลาที่ยากลำบากในการผูกคุณสมบัติสตริงแบบคงที่ง่าย ๆ กับกล่องข้อความ

นี่คือคลาสที่มีคุณสมบัติแบบคงที่:

public class VersionManager
{
    private static string filterString;

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

ใน xaml ของฉันฉันต้องการผูกคุณสมบัติสแตติกนี้กับกล่องข้อความ:

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

ทุกอย่างรวบรวม แต่ในเวลาทำงานฉันได้รับข้อยกเว้นต่อไปนี้:

ไม่สามารถแปลงค่าในแอตทริบิวต์ 'แหล่งที่มา' เป็นวัตถุประเภท 'System.Windows.Markup.StaticExtension' ข้อผิดพลาดที่วัตถุ 'System.Windows.Data.Binding' ในไฟล์มาร์กอัป 'BurnDisk; component / selectversionpagefunction.xaml' บรรทัด 57 ตำแหน่ง 29

มีความคิดอะไรที่ฉันทำผิดหรือเปล่า?

คำตอบ:


168

หากการโยงต้องเป็นแบบสองทางคุณต้องระบุพา ธ มีเคล็ดลับในการทำการผูกสองทางกับคุณสมบัติสแตติกหากคลาสไม่คงที่: ประกาศอินสแตนซ์จำลองของคลาสในทรัพยากรและใช้เป็นแหล่งที่มาของการผูก

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

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

คำตอบนี้เหมาะสมกับกรณีของฉันมากกว่าเพราะฉันไม่ต้องการแนะนำ DependencyObject ให้กับคลาสซอร์สของฉัน ขอบคุณสำหรับทิป!
Anthony Brien

6
โปรดทราบว่าจะเปิดใช้งานกล่องข้อความของคุณเพื่อส่งค่ากลับไปยังคุณสมบัติแบบคงที่ แต่จะไม่อัปเดตกล่องข้อความเมื่อค่าแหล่งข้อมูลเปลี่ยนแปลง
Adam Sills

1
ไม่เป็นไรฉันแค่ต้องการการเชื่อมจากกล่องข้อความถึงแหล่งที่มาในกรณีนี้ ถ้าฉันต้องการให้การเชื่อมโยงทำงานในลักษณะอื่นฉันรู้ถึงความต้องการวิธีการอย่างใดอย่างหนึ่งต่อไปนี้: INotifyPropertyChanged, <PropertyName> เหตุการณ์ที่เปลี่ยนแปลงหรือคุณสมบัติการพึ่งพา
Anthony Brien

1
หมายเหตุ: วิธีนี้จะไม่ทำงานในสถานการณ์ MVVM เนื่องจากโดยทั่วไปคุณไม่สามารถเข้าถึงประเภทของวัตถุที่คุณผูกไว้ได้
Antony Woods

@ โทมัสฉันชอบที่จะให้สิ่งนี้ใช้งานได้สำหรับฉัน แต่ทำไม่ได้ ฉันโพสต์ภาวะที่กลืนไม่เข้าคายไม่ออกของฉันเป็นคำถามอื่นที่นี่: stackoverflow.com/questions/34656670/ …
Andrew Simpson

107

คุณไม่สามารถผูกกับแบบคงที่เช่นนั้น ไม่มีวิธีใดที่โครงสร้างพื้นฐานการเชื่อมโยงจะได้รับแจ้งการปรับปรุงเนื่องจากไม่มีส่วนเกี่ยวข้องDependencyObject(หรืออินสแตนซ์ของวัตถุที่ใช้INotifyPropertyChanged)

หากค่านั้นไม่เปลี่ยนแปลงให้ทิ้งการผูกไว้และใช้x:StaticภายในTextคุณสมบัติโดยตรง กำหนดappด้านล่างให้เป็นที่ตั้ง namespace (และแอสเซมบลี) ของคลาส VersionManager

<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: sys แบบคงที่: Int32.MaxValue}, Mode = OneWay} "/> นั่นคือ ทำงานเพราะมันเป็นวิธีหนึ่ง?
แอนโธนีไบรอัน

2
ใช่การผูกมัดแบบสองทางใด ๆ ต้องมีค่าคุณสมบัติเส้นทางในการผูก แหล่งที่มาจะต้องเป็นวัตถุที่มีคุณสมบัติที่ระบุโดยเส้นทาง การระบุ 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; 

โปรดสังเกตว่าในกรณีแรกคลาส exposes เหตุการณ์แบบคงที่ชื่อ PropertyNameChanged ที่ส่งผ่าน EventArgs ไปยังตัวจัดการเหตุการณ์ ในกรณีที่สองคลาส exposes เหตุการณ์แบบคงที่ชื่อ StaticPropertyChanged ที่ผ่าน PropertyChangedEventArgs ไปยังตัวจัดการเหตุการณ์ คลาสที่ใช้คุณสมบัติสแตติกสามารถเลือกยกระดับการแจ้งเตือนการเปลี่ยนแปลงคุณสมบัติโดยใช้วิธีใดวิธีหนึ่ง


นี่คือลิงค์ในกรณีที่ทุกคนต้องการอ่านเพิ่มเติม Microsoft เอามันลง แต่เก็บไว้ในเว็บที่นี่ web.archive.org/web/20131129053934/http://msdn.microsoft.com/?hl=th
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เป็น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

ดูโครงการของฉันCalcBindingซึ่งให้คุณเขียนนิพจน์ที่ซับซ้อนในค่าคุณสมบัติเส้นทางรวมถึงคุณสมบัติคงที่คุณสมบัติแหล่งที่มาคณิตศาสตร์และอื่น ๆ ดังนั้นคุณสามารถเขียนสิ่งนี้:

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

โชคดี!


0

คำตอบเหล่านี้เป็นสิ่งที่ดีถ้าคุณต้องการที่จะปฏิบัติตามอนุสัญญาที่ดี แต่ OP ต้องการสิ่งที่ง่ายซึ่งเป็นสิ่งที่ฉันต้องการด้วยแทนที่จะจัดการกับรูปแบบการออกแบบ GUI หากสิ่งที่คุณต้องการคือมีสตริงในแอพ GUI พื้นฐานที่คุณสามารถอัปเดตเฉพาะกิจโดยไม่ต้องกังวลอะไรคุณสามารถเข้าถึงได้โดยตรงในซอร์ส 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's 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----";
        }
    }
}

จากนั้นเมื่อคุณทริกเกอร์เหตุการณ์การคลิกนั้นโดยคลิกที่ปุ่ม voila! Kilroy ปรากฏขึ้น :)

ป้อนคำอธิบายรูปภาพที่นี่


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.