Tworzenie powiązania WPF TextBox na każdym nowym znaku?

85

Jak mogę zaktualizować powiązanie danych, gdy tylko zostanie wpisany nowy znak w TextBox?

Uczę się o powiązaniach w WPF i teraz utknąłem w (miejmy nadzieję) prostej sprawie.

Mam prostą klasę FileLister, w której można ustawić właściwość Path, a następnie wyświetli listę plików, gdy uzyskasz dostęp do właściwości FileNames. Oto ta klasa:

class FileLister:INotifyPropertyChanged {
    private string _path = "";

    public string Path {
        get {
            return _path;
        }
        set {
            if (_path.Equals(value)) return;
            _path = value;
            OnPropertyChanged("Path");
            OnPropertyChanged("FileNames");
        }
    }

    public List<String> FileNames {
        get {
            return getListing(Path);
        }
    }

    private List<string> getListing(string path) {
        DirectoryInfo dir = new DirectoryInfo(path);
        List<string> result = new List<string>();
        if (!dir.Exists) return result;
        foreach (FileInfo fi in dir.GetFiles()) {
            result.Add(fi.Name);
        }
        return result;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string property) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) {
            handler(this, new PropertyChangedEventArgs(property));
        }
    }
}

Używam FileLister jako StaticResource w tej bardzo prostej aplikacji:

<Window x:Class="WpfTest4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfTest4"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:FileLister x:Key="fileLister" Path="d:\temp" />
    </Window.Resources>
    <Grid>
        <TextBox Text="{Binding Source={StaticResource fileLister}, Path=Path, Mode=TwoWay}"
        Height="25" Margin="12,12,12,0" VerticalAlignment="Top" />
        <ListBox Margin="12,43,12,12" Name="listBox1" ItemsSource="{Binding Source={StaticResource ResourceKey=fileLister}, Path=FileNames}"/>
    </Grid>
</Window>

Wiązanie działa. Jeśli zmienię wartość w polu tekstowym, a następnie kliknę poza nim, zawartość pola listy zostanie zaktualizowana (o ile istnieje ścieżka).

Problem w tym, że chciałbym zaktualizować, gdy tylko zostanie wpisany nowy znak, a nie czekać, aż pole tekstowe straci fokus.

Jak mogę to zrobić? Czy istnieje sposób, aby to zrobić bezpośrednio w XAML, czy też muszę obsługiwać zdarzenia TextChanged lub TextInput w pudełku?

luddet
źródło

Odpowiedzi:

146

W oprawie Textbox wszystko, co musisz zrobić, to ustawić UpdateSourceTrigger=PropertyChanged.

Dave
źródło
1
Dzięki! Dokładnie takie proste rozwiązanie, na jakie
liczyłem
U mnie to nie zadziałało ... Chcę przywrócić tekst do poprzedniej wartości, na wypadek, gdyby nie była to liczba. Tylko po dodaniu IsAsync = True zadziałało.
ilans
Próbowałem ustawić to w projektancie Visual Studio (VS2015). W oknie dialogowym powiązania opcja jest wyświetlana, gdy rozwijam ekspander „Więcej ustawień”. Jednak UpdateSourceTrigger jest wyłączony, chyba że ustawię również BindingDirection na coś innego niż Default.
Martin Brown
32

Musisz ustawić UpdateSourceTriggerwłaściwość naPropertyChanged

<TextBox Text="{Binding Source={StaticResource fileLister}, Path=Path, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
         Height="25" Margin="12,12,12,0" VerticalAlignment="Top"/>
Eduardo Brites
źródło
-1

Bez C # wystarczy w języku XAML dla TextBox, a nie dla klasy. A więc monitorowanie właściwości TextBlock, gdzie zapisuje się długość TextBox: Binding Text.Length

<StackPanel>
  <TextBox x:Name="textbox_myText" Text="123" />
  <TextBlock x:Name="tblok_result" Text="{Binding Text.Length, ElementName=textbox_myText}"/>
</StackPanel>
sg6336
źródło
-2

Nagle wiązanie danych między suwakiem a powiązanym TextBoxem spowodowało problemy. W końcu znalazłem przyczynę i mogłem to naprawić. Konwerter, którego używam:

using System;
using System.Globalization;
using System.Windows.Data;
using System.Threading;

namespace SiderExampleVerticalV2
{
    internal class FixCulture
    {
        internal static System.Globalization.NumberFormatInfo currcult
                = Thread.CurrentThread.CurrentCulture.NumberFormat;

        internal static NumberFormatInfo nfi = new NumberFormatInfo()
        {
            /*because manual edit properties are not treated right*/
            NumberDecimalDigits = 1,
            NumberDecimalSeparator = currcult.NumberDecimalSeparator,
            NumberGroupSeparator = currcult.NumberGroupSeparator
        };
    }

    public class ToOneDecimalConverter : IValueConverter
    {
        public object Convert(object value,
            Type targetType, object parameter, CultureInfo culture)
        {
            double w = (double)value;
            double r = Math.Round(w, 1);
            string s = r.ToString("N", FixCulture.nfi);
            return (s as String);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string s = (string)value;
            double w;
            try
            {
                w = System.Convert.ToDouble(s, FixCulture.currcult);
            }
            catch
            {
                return null;
            }
            return w;
        }
    }
}

W XAML

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

dalej zdefiniowany TextBox

<TextBox x:Name="TextSlidVolume"
    Text="{Binding ElementName=SlidVolume, Path=Value,
        Converter={StaticResource ToOneDecimalConverter},Mode=TwoWay}"
/>
Erhy
źródło
2
Myślę, że umieściłeś swoją odpowiedź w złym pytaniu. Oryginalne pytanie nie zawiera żadnych informacji o suwaku.
GrantByrne