2009-11-19 14 views
6

Jak pominąć aktualizację niektórych sub-wiązań o MultiBinding? Mam zdefiniowane w kodzie-za (miałem pewne kłopoty czyni go w XAML i nie sądzę, jest to ważne - w końcu kodu źródłowego nie mniej ekspresyjny jest następnie XAML) A MultiBinding który trwa dwie właściwości tylko do odczytu i jedno normalne właściwość do wytworzenia jednej wartości. W przypadku ConvertBack właściwości tylko do odczytu nie są modyfikowane (oni podtrzymywać ich wartości), a tylko normalna właściwość zostanie zmieniona.TwoWay MultiBinding z tylko do odczytu właściwości

Podczas definicji MultiBinding cała MultiBinding został ustawiony TwoWay jednak poszczególne pod-wiązanie, w którym ustala odpowiednie (pierwsze dwa OneWay i trzeci dwóch TwoWay).


Problem występuje we własnym zakresie. Jednak ze względu na prezentację uprośniłem go do mniejszej kontroli. Kontrolą przedstawioną w tym przykładzie jest kontrola podobna do Slider, pozwalająca wybrać wartość w [0,0; 1,0] zasięgu. Wybrana wartość jest reprezentowana przez kciuk i jest wyświetlana jako DependencyProperty.

Zasadniczo, kontrola jest zbudowany przez 1 wiersz x 3 kolumny Grid gdzie kciuk znajduje się w środkowej kolumnie. Aby prawidłowo ustawić lewą kolumnę kciuka, należy przypisać szerokość odpowiadającą wybranej pozycji. Jednakże szerokość ta zależy również od rzeczywistej szerokości całego sterowania i rzeczywistej szerokości samego kciuka (to dlatego, że położenie jest podana jako wartość względną w [0,0; 1,0] Zakres).

Gdy kciuk zostanie przeniesiony stanowisko powinno zostać zaktualizowane jednak odpowiednio szerokość i szerokość kciuka kontrola oczywiście nie zmieni.

Kod działa zgodnie z oczekiwaniami, ale po uruchomieniu w IDE podczas przemieszczania kciuka Okno wyjściowe jest zaśmiecone informacjami o wyjątkach zgłaszanymi, gdy MultiBinding próbuje ustawić wartość tych dwóch właściwości tylko do odczytu. Podejrzewam, że nie jest to szkodliwe, ale jest nieco denerwujące i mylące. A także oznacza, że ​​kod robi coś innego, niż chciałem, aby to zrobić, ponieważ nie chciałem ustawiać tych właściwości (ma to znaczenie na wypadek, gdyby nie były tylko do odczytu, a to by je zmodyfikowało).

MultiBindingdocumentation w sekcji Uwagi wspomina, że ​​poszczególne powiązania podrzędne mogą przesłonić wartość trybu MultiBinding, ale wydaje się, że nie działa.

Być może można to w jakiś sposób rozwiązać, wyrażając zależność od kontroli i szerokości kciuka (właściwości tylko do odczytu) w jakiś sposób inaczej. Na przykład osobno rejestruje się powiadomienia i wymusza aktualizację po ich zmianie. Jednak nie wydaje mi się to naturalne. MultiBinding działa z drugiej strony, ponieważ po lewej szerokości kolumny zależy od tych trzech właściwości.


Oto przykład kodu XAML.

<UserControl x:Class="WpfTest.ExampleUserControl" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
<Grid> 
    <Grid.RowDefinitions> 
    <RowDefinition /> 
    </Grid.RowDefinitions> 
    <Grid.ColumnDefinitions> 
    <ColumnDefinition x:Name="leftColumn" /> 
    <ColumnDefinition x:Name="thumbColumn" Width="Auto" /> 
    <ColumnDefinition /> 
    </Grid.ColumnDefinitions> 
    <!-- Rectangle used in the left column for better visualization. --> 
    <Rectangle Grid.Column="0"> 
    <Rectangle.Fill> 
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> 
    <GradientStop Color="Black" Offset="0" /> 
    <GradientStop Color="White" Offset="1" /> 
    </LinearGradientBrush> 
    </Rectangle.Fill> 
    </Rectangle> 
    <!-- Thumb representing the Position property. --> 
    <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center" /> 
    <!-- Rectangle used in the right column for better visualization. --> 
    <Rectangle Grid.Column="2"> 
    <Rectangle.Fill> 
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> 
    <GradientStop Color="White" Offset="0" /> 
    <GradientStop Color="Black" Offset="1" /> 
    </LinearGradientBrush> 
    </Rectangle.Fill> 
    </Rectangle> 
</Grid> 
</UserControl> 

I tu jest odpowiedni kod z opóźnieniem

using System; 
using System.Globalization; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 

namespace WpfTest 
{ 
public partial class ExampleUserControl : UserControl 
{ 
    #region PositionConverter 

    private class PositionConverter : IMultiValueConverter 
    { 
    public PositionConverter(ExampleUserControl owner) 
    { 
    this.owner = owner; 
    } 

    #region IMultiValueConverter Members 

    public object Convert(
    object[] values, 
    Type targetType, 
    object parameter, 
    CultureInfo culture) 
    { 
    double thisActualWidth = (double)values[0]; 
    double thumbActualWidth = (double)values[1]; 
    double position = (double)values[2]; 

    double availableWidth = thisActualWidth - thumbActualWidth; 

    double leftColumnWidth = availableWidth * position; 

    return new GridLength(leftColumnWidth); 
    } 

    public object[] ConvertBack(
    object value, 
    Type[] targetTypes, 
    object parameter, 
    CultureInfo culture) 
    { 
    double thisActualWidth = owner.ActualWidth; 
    double thumbActualWidth = owner.thumbColumn.ActualWidth; 
    GridLength leftColumnWidth = (GridLength)value; 

    double availableWidth = thisActualWidth - thumbActualWidth; 

    double position; 
    if (availableWidth == 0.0) 
    position = 0.0; 
    else 
    position = leftColumnWidth.Value/availableWidth; 

    return new object[] { 
    thisActualWidth, thumbActualWidth, position 
    }; 
    } 

    #endregion 

    private readonly ExampleUserControl owner; 
    } 

    #endregion 

    public ExampleUserControl() 
    { 
    InitializeComponent(); 

    MultiBinding leftColumnWidthBinding = new MultiBinding() 
    { 
    Bindings = 
    { 
    new Binding() 
    { 
     Source = this, 
     Path = new PropertyPath("ActualWidth"), 
     Mode = BindingMode.OneWay 
    }, 
    new Binding() 
    { 
     Source = thumbColumn, 
     Path = new PropertyPath("ActualWidth"), 
     Mode = BindingMode.OneWay 
    }, 
    new Binding() 
    { 
     Source = this, 
     Path = new PropertyPath("Position"), 
     Mode = BindingMode.TwoWay 
    } 
    }, 
    Mode = BindingMode.TwoWay, 
    Converter = new PositionConverter(this) 
    }; 
    leftColumn.SetBinding(
    ColumnDefinition.WidthProperty, leftColumnWidthBinding); 
    } 

    public static readonly DependencyProperty PositionProperty = 
    DependencyProperty.Register(
    "Position", 
    typeof(double), 
    typeof(ExampleUserControl), 
    new FrameworkPropertyMetadata(0.5) 
    ); 

    public double Position 
    { 
    get 
    { 
    return (double)GetValue(PositionProperty); 
    } 
    set 
    { 
    SetValue(PositionProperty, value); 
    } 
    } 

} 
} 

Odpowiedz

9

Wreszcie sam znalazłem rozwiązanie. W rzeczywistości jest to w documentation - nie wiem, jak to przegapiłem, ale zapłaciłem za to drogo (w straconym czasie).

Zgodnie z dokumentacją ConvertBack powinna powrócić Binding.DoNothing stanowiskami, na których nie ma wartość ma być ustawiony (w szczególności nie było OneWay pożądane jest wiązanie). Inną specjalną wartością jest DependencyProperty.UnsetValue.

To nie jest kompletne rozwiązanie, ponieważ teraz wdrożenie IMultiValueConverter musi wiedzieć, gdzie zwrócić specjalną wartość. Sądzę jednak, że rozwiązanie to obejmuje najbardziej uzasadnione przypadki.

+0

Dzięki; tego właśnie potrzebowałem wiedzieć! Czy istnieje prosty sposób uzyskania wartości wejściowych tych właściwości w ConvertBack? –

+1

Dzięki! Warto również wspomnieć o indywidualnych "wewnętrznych" wiązaniach, które mają zostać przekonwertowane na wartość, i które powinny być ustawione na "Mode = TwoWay". Nie znalazłem tego w dokumentacji. : / –

4

Wygląda MultiBinding nie działa dobrze. Widziałem pewne nieoczekiwane zachowanie (coś podobnego do twojego) wcześniej w mojej praktyce. Możesz także wstawiać punkty przerwania lub niektóre śledzenie w konwerterze i możesz znaleźć zabawne rzeczy o tym, którzy konwertery i kiedy są wywoływani. Jeśli to możliwe, należy unikać używania funkcji MultiBinding. Na przykład.możesz dodać specjalną właściwość w swoim modelu widoku, która ustawi wartość właściwości mutable w swoim ustawiaczu i zwróci potrzebną wartość, używając wszystkich trzech swoich właściwości w swoim pobierającym. Jest to coś jak MultiValueConverter wewnątrz właściwości =).

Mam nadzieję, że to pomaga.

+0

Jeśli to naprawdę jest jakiś błąd w MultiBinding, to może powinniśmy go jakoś zgłosić. Gdzie to zrobić? –

+0

Czy próbowałeś zrobić to samo w .NET 4.0? Może to jest teraz naprawione. (Nie mogę teraz spróbować z powodu problemów technicznych z moim VS 2010). – levanovd

Powiązane problemy