Dwukierunkowe wiązanie pola tekstowego do suwaka w WPF
Moim największym wyzwaniem jest znalezienie sposobu na rozwiązanie problemu, który mam z wiązaniem danych do suwaka i pola tekstowego.
Dostosowywanie
:
aktualna wartość suwaka jest wyświetlana wewnątrz pola tekstowego. Gdy użytkownik przeciągnie suwak, wartość zostanie odzwierciedlona w polu tekstowym. Użytkownik może przeciągnąć i upuścić suwak do wybranej wartości, kliknąć dowolne miejsce na ścieżce suwaka, aby ustawić wartość, lub wprowadzić wartość ręcznie w polu tekstowym. W tym drugim przypadku wartość wpisana w polu tekstowym powinna aktualizować pozycję suwaka.
Texbox jest dwukierunkowo powiązany z właściwością datacontext, podczas gdy suwak jest jednokierunkowo powiązany z tą samą właściwością. Gdy użytkownik przesuwa lub klika suwak trackera, używam zdarzenia dragcompleted suwaka, aby powiadomić kontekst danych o zmianie. Z drugiej strony, gdy użytkownik kliknie moduł śledzący, używam zdarzenia OnValueChanged suwaka, aby powiadomić kontekst danych (i używam flagi, aby upewnić się, że OnValueChanged nie został wyzwolony przez przesunięcie suwaka)
Problem
: Zdarzenie OnValueChanged jest wyzwalane nawet wtedy, gdy wartość suwaka jest inicjowana z wartością powiązania, więc nie mogę dowiedzieć się, czy ta wartość faktycznie pochodzi od użytkownika, czy z powiązania.
Czy możesz zasugerować alternatywny sposób wykonania wiązania, aby upewnić się, że możemy odróżnić aktualizację użytkownika od powiązania aktualizacji dla suwaka?
Dziękuję!
Aktualizacja
Przepraszam, zapomniałem wspomnieć, dlaczego nie łączę zarówno suwaka, jak i pola tekstowego bezpośrednio na dwa sposoby, jak sugerują poniższe odpowiedzi. Oczekuje się, że aktualizacja wartości kontekstu danych spowoduje wywołanie serwera zaplecza i pobranie danych z bazy danych. Problem polega na tym, że gdy użytkownik przeciąga suwak, powoduje on aktualizacje. Obchodzę ten problem, polegając tylko na rzeczywistym zdarzeniu onValueChanged, aby wywołać metodę DoWhthing. Mam nadzieję, że to jest trochę jaśniejsze. Przepraszam, że to pomijam ...
Szybko zebrałem próbkę poniżej, abyś mógł spróbować.
W XAML
:
<Window x:Class="SliderIssue.MainWindow"Na kod
xmlns="[url=http://schemas.microsoft.com/winfx/2006/xaml/presentation"]http://schemas.microsoft.com/w ... ot%3B[/url]
xmlns:x="[url=http://schemas.microsoft.com/winfx/2006/xaml"]http://schemas.microsoft.com/winfx/2006/xaml"[/url]
Title="MainWindow" Height="350" Width="525">
<Grid HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions> <Slider Name="slider" VerticalAlignment="Top"
ValueChanged="slider_ValueChanged"
Thumb.DragStarted="slider_DragStarted"
Thumb.DragCompleted="slider_DragCompleted"
Value="{Binding Count}"
Width="200"
Minimum="0"
Maximum="100"/>
<TextBox VerticalAlignment="Top"
HorizontalAlignment="Left"
Grid.Column="1"
Width="100"
Text="{Binding Count,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
Height="25"/>
</Grid>
:
using System.Windows;namespace SliderIssueProsta klasa danych
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool _dragStarted; public MainWindow()
{
InitializeComponent(); var item = new Item();
DataContext = item;
} private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_dragStarted)
{
var item = (Item)DataContext;
item.DoWhatever(e.NewValue);
}
} private void slider_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
{
_dragStarted = true;
} private void slider_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
{
_dragStarted = false; var item = (Item) DataContext;
item.DoWhatever(slider.Value);
}
}
}
:
using System.ComponentModel;namespace SliderIssue
{
public class Item : INotifyPropertyChanged
{
private int _count = 50;
public int Count
{
get { return _count; }
set
{
if (_count != value)
{
_count = value;
DoWhatever(_count);
OnPropertyChanged("Count");
}
}
} public void DoWhatever(double value)
{
//do something with value
//and blablabla
} public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
Nie znaleziono powiązanych wyników
Zaproszony:
Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się
2 odpowiedzi
Anonimowy użytkownik
Potwierdzenie od:
Mniej więcej tak samo jak z Ericiem, ale jako osobna propozycja operacji.
W ten sposób możesz zarządzać aktualizacjami, nawet jeśli użytkownik oszaleje.
Myślę, że za dużo o tym myślałeś. Usuń wszystkie wydarzenia z suwaka i pola tekstowego. Jeśli pierwsza wartość (ustawiana programowo) nie powinna wywoływać metody DoWhewhere, zaznacz pole w tym kodzie, aby pominąć pierwszą inicjalizację ...
Zalecam powiązanie suwaka z Count jako TwoWay i wymuszenie na właściwości Count wykonywania dowolnego procesu (jak pokazano w klasie encji). Nie ma potrzeby sprawdzania kliknięć ani żadnych innych zdarzeń. Jeśli użytkownik zmieni wartość w polu tekstowym, zmieni suwak i odwrotnie.
Anonimowy użytkownik
Potwierdzenie od:
UPDATE
39, teraz rozumiem, dlaczego próbowałeś to zrobić w ten sposób. Mam kilka sugestii, które mogą pomóc.
Mój pierwszy jest trochę bardziej zarozumiały, ale mimo to sugeruję. Jeśli problemem, który próbujesz rozwiązać, jest dławienie zapytań do wewnętrznej bazy danych, powiedziałbym, że Twój ViewModel nie powinien się tym martwić. Popchnąłbym to w dół warstwy do obiektu, który wywołuje zaplecze w oparciu o zaktualizowaną wartość przekazaną z ViewModel.
Możesz stworzyć próbę ograniczania przepustowości przez biedaka, pisząc za każdym razem, gdy wywoływana jest metoda w celu wysłania zapytania do zaplecza DB. Porównaj tę wartość z ostatnio zapisaną wartością. Jeśli TimeSpan między spadnie poniżej minimalnego progu, zaktualizuj wartość, z którą został porównany, i zignoruj żądanie.
Możesz zrobić to samo z timerem i zresetować timer za każdym razem, gdy zostanie wysłane żądanie, ale jest to jeszcze bardziej zagmatwane.
Gdy wywołanie powraca z zaplecza, ta warstwa wywołuje zdarzenie, które obsługuje ViewModel i robi wszystko, co musi zrobić ze zwróconymi danymi.
Jako kolejna sugestia chciałbym również sprawdzić, co daje Ci ReactiveExtensions. Zrozumienie, jak działają, zajmuje trochę czasu, ale możesz utworzyć na podstawie przepływu zdarzenia, a następnie użyć metody , aby zwrócić kolejną . Subskrybujesz to i tam dzwonisz. Wymagałoby to ponownego przemyślenia projektu i architektury oprogramowania, ale jest to intrygujące.
Paul Betts stworzył cały framework MVVM oparty na Rx o nazwie
ReactiveUI
http://www.reactiveui.net... Po raz pierwszy dowiedziałem się o ograniczaniu obserwałów w jednym z jego postów na blogu
tutaj
http://blog.paulbetts.org/inde ... lper/
.
Powodzenia!
ORYGINALNY POST
Jeśli dobrze rozumiem twój problem, wygląda na to, że chciałbyś, aby zarówno suwak, jak i TextBox odzwierciedlały tę samą właściwość DataContext (zwykle ViewModel). Wygląda na to, że próbujesz zduplikować to, co daje mechanizm wiązania WPF. Udało mi się szybko uzyskać prototyp tej pracy. Oto kod, którego użyłem.
Dla widoku właśnie utworzyłem nowe okno z tym jako zawartością okna.
Zwróć uwagę, że zarówno suwak, jak i TextBox są powiązane z tą samą (sprytnie nazwaną) wartością DataContext. Gdy użytkownik wprowadzi nową wartość do TextBox, wartość zmieni się, a powiadomienie o zmianie właściwości (w ViewModel) spowoduje, że suwak automatycznie zaktualizuje swoją wartość.
Oto kod dla ViewModel (tj. DataContext widoku).
Mój ViewModel pochodzi z klasy ViewModelBase, która implementuje interfejs . Metoda jest zdefiniowana w klasie bazowej, która po prostu wywołuje zdarzenie dla właściwości, której nazwa jest przekazywana jako parametr.
Na koniec utworzyłem View i przypisałem nową instancję ViewModel jako jego DataContext (zrobiłem to bezpośrednio w metodzie aplikacji dla tego przykładu).
Mam nadzieję, że pomoże ci to podążać we właściwym kierunku.