Enlace a propiedad estática

168

Me resulta difícil vincular una propiedad de cadena estática simple a un cuadro de texto.

Aquí está la clase con la propiedad estática:

public class VersionManager
{
    private static string filterString;

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

En mi xaml, solo quiero vincular esta propiedad estática a un TextBox:

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

Todo se compila, pero en tiempo de ejecución, obtengo la siguiente excepción:

No se puede convertir el valor en el atributo 'Fuente' al objeto de tipo 'System.Windows.Markup.StaticExtension'. Error en el objeto 'System.Windows.Data.Binding' en el archivo de marcado 'BurnDisk; componente / selectversionpagefunction.xaml' Línea 57 Posición 29.

¿Alguna idea de lo que estoy haciendo mal?

Anthony Brien
fuente

Respuestas:

168

Si el enlace debe ser bidireccional, debe proporcionar una ruta. Hay un truco para hacer un enlace bidireccional en una propiedad estática, siempre que la clase no sea estática: declare una instancia ficticia de la clase en los recursos y úsela como la fuente del enlace.

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

<TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>
Thomas Levesque
fuente
Esta respuesta es más apropiada para mi caso porque no quiero presentar DependencyObject a mi clase fuente. ¡Gracias por el consejo!
Anthony Brien
66
Tenga en cuenta que habilitará su cuadro de texto para devolver el valor a la propiedad estática, pero no actualizará el cuadro de texto cuando cambie el valor de origen.
Adam Sills
1
Eso está bien, solo necesitaba el enlace desde el cuadro de texto a la Fuente en este caso. Si quiero que el enlace funcione de otra manera, soy consciente de la necesidad de uno de estos métodos: INotifyPropertyChanged, <PropertyName> Modificado evento o propiedad de dependencia.
Anthony Brien
1
Nota: Esta solución no funcionará en una situación MVVM, ya que generalmente no tiene acceso a los tipos de objetos a los que está vinculado.
Antony Woods
@thomas Me encantaría que esto funcione para mí, pero no puedo. Publiqué mi dilema como otra pregunta aquí: stackoverflow.com/questions/34656670/…
Andrew Simpson
107

No puedes unirte a una estática como esa. No hay forma de que la infraestructura de enlace reciba notificaciones de actualizaciones ya que no hay DependencyObject(o instancia de objeto que implemente INotifyPropertyChanged) involucrada.

Si ese valor no cambia, simplemente abandone el enlace y úselo x:Staticdirectamente dentro de la Textpropiedad. Defina a appcontinuación la ubicación del espacio de nombres (y ensamblaje) de la clase VersionManager.

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

Si el valor cambia, sugeriría crear un singleton para contener el valor y enlazarlo.

Un ejemplo del 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}"/>
Adam Sills
fuente
55
De Verdad? He podido hacer un enlace al Int32.MaxValue estático que es muy similar a mi muestra: <TextBox Text = {Binding Source = {x: Static sys: Int32.MaxValue}, Mode = OneWay} "/> ¿Es eso? de trabajo porque es una manera?
Anthony Brien
2
Sí, cualquier enlace bidireccional requiere un valor de propiedad Path en el enlace. El origen debe ser un objeto que contenga la propiedad especificada por Path. Al especificar OneWay, se elimina esa restricción.
Adam Sills
Además, perdón por la actualización tardía, pero actualicé la respuesta anterior con una muestra.
Adam Sills
¿Hay alguna manera de vincular una cadena estática? Tengo un enlace múltiple y una de las entradas es una cadena fija.
Nitin Chaudhari
39

En .NET 4.5 es posible enlazar a propiedades estáticas, leer más

Puede usar propiedades estáticas como fuente de un enlace de datos. El motor de enlace de datos reconoce cuándo cambia el valor de la propiedad si se genera un evento estático. Por ejemplo, si la clase SomeClass define una propiedad estática llamada MyProperty, SomeClass puede definir un evento estático que se genera cuando cambia el valor de MyProperty. El evento estático puede usar cualquiera de las siguientes firmas:

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

Tenga en cuenta que en el primer caso, la clase expone un evento estático llamado PropertyNameChanged que pasa EventArgs al controlador de eventos. En el segundo caso, la clase expone un evento estático llamado StaticPropertyChanged que pasa PropertyChangedEventArgs al controlador de eventos. Una clase que implementa la propiedad estática puede optar por generar notificaciones de cambio de propiedad utilizando cualquiera de los métodos.

Jowen
fuente
Aquí está el enlace en caso de que alguien quiera leer más. Microsoft lo eliminó, pero está en el archivo web aquí. web.archive.org/web/20131129053934/http://msdn.microsoft.com/…
C. Tewalt
Esta respuesta me señaló en la dirección correcta, pero aún me tomó un tiempo resolver los detalles sin un ejemplo. He escrito un ejemplo basado en el código original.
Matt
13

A partir de WPF 4.5, puede enlazar directamente a propiedades estáticas y hacer que el enlace se actualice automáticamente cuando se cambia su propiedad. Es necesario cablear manualmente un evento de cambio para activar las actualizaciones de enlace.

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

}

Ahora puede vincular su propiedad estática como cualquier otra:

<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
Mate
fuente
1
La VersionManagerclase puede ser estática y todo sigue funcionando. Tenga en cuenta las llaves en la definición de ruta Path=(local:VersionManager.FilterString). ¿Alguien sabe por qué son realmente necesarios?
chviLadislav
2
Los corchetes en la definición de ruta son necesarios porque la propiedad es estática, vea aquí
chviLadislav
11

Podría haber dos formas / sintaxis para vincular una staticpropiedad. Si p es una staticpropiedad en clase MainWindow, entonces bindingfor textboxserá:

1)

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

2)

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

Puedes usar la ObjectDataProviderclase y es MethodNamepropiedad. Puede verse así:

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

El proveedor de datos de objetos declarados se puede usar así:

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

Si está utilizando recursos locales, puede consultarlos de la siguiente manera:

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

Variante correcta para .NET 4.5 +

Código 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;
}

Enlace XAML (atención a las llaves son (), no {})

<TextBox Text="{Binding Path=(yournamespace:VersionManager.FilterString)}" />
Alexei Shcherbakov
fuente
Hizo un ligero cambio en su código para llamar correctamente al EventHandler.
Mark A. Donohoe
Intenté muchas soluciones diferentes y esta funcionó. El PropertyChangedEventHandler es lo que funcionó para mí. Salud.
Mgamerz
2

Mire mi proyecto CalcBinding , que le proporciona escribir expresiones complejas en el valor de la propiedad Path, incluidas las propiedades estáticas, las propiedades de origen, las matemáticas y otras. Entonces, puedes escribir esto:

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

¡Buena suerte!

Alex141
fuente
0

Todas estas respuestas son buenas si quiere seguir buenas convenciones pero el OP quería algo simple , que es lo que yo también quería en lugar de tratar con patrones de diseño de GUI. Si todo lo que desea hacer es tener una cadena en una aplicación GUI básica que puede actualizar ad-hoc sin nada sofisticado, puede acceder directamente a ella en su fuente C #.

Digamos que tienes una aplicación WPF realmente básica MainWindow XAML como esta,

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

Eso se verá más o menos así:

ingrese la descripción de la imagen aquí

En la fuente de su MainWindow XAML, podría tener algo como esto en todo lo que estamos haciendo para cambiar el valor directamente a través de textBlock.Text's get/ setfuncionalidad:

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

Luego, cuando active ese evento de clic haciendo clic en el botón, ¡listo! Kilroy aparece :)

ingrese la descripción de la imagen aquí

kayleeFrye_onDeck
fuente
0

Otra solución es crear una clase normal que implemente PropertyChanger como este

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

Luego cree una instancia estática de la clase en algún lugar que no desee

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

Y ahora úsalo como propiedad estática

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

Y aquí está la implementación de PropertyChanger si es necesario

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));
    }
}
neosonne
fuente
-1

Respuesta más eficaz (.net 4.5 y posterior):

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

y XAML:

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

No descuides los corchetes

Sean
fuente