Tak, robiłem to w przeszłości z właściwościami ActualWidth
i ActualHeight
, które są tylko do odczytu. Utworzyłem dołączone zachowanie, które ma ObservedWidth
i ObservedHeight
dołączone właściwości. Ma również Observe
właściwość używaną do wykonywania początkowego podłączenia. Sposób użycia wygląda następująco:
<UserControl ...
SizeObserver.Observe="True"
SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}"
Dlatego model widoku ma właściwości Width
i Height
, które są zawsze zsynchronizowane z właściwościami ObservedWidth
i ObservedHeight
dołączonymi. Observe
Nieruchomość prostu przywiązuje do SizeChanged
zdarzenia z FrameworkElement
. W uchwycie aktualizuje swoje ObservedWidth
i ObservedHeight
właściwości. Ergo, Width
and Height
modelu widoku jest zawsze zsynchronizowany z ActualWidth
and ActualHeight
of the UserControl
.
Może nie jest to idealne rozwiązanie (zgadzam się - DP tylko do odczytu powinny obsługiwać OneWayToSource
wiązania), ale działa i utrzymuje wzorzec MVVM. Oczywiście, ObservedWidth
ani ObservedHeight
DP nie są tylko do odczytu.
UPDATE: oto kod, który implementuje opisaną powyżej funkcjonalność:
public static class SizeObserver
{
public static readonly DependencyProperty ObserveProperty = DependencyProperty.RegisterAttached(
"Observe",
typeof(bool),
typeof(SizeObserver),
new FrameworkPropertyMetadata(OnObserveChanged));
public static readonly DependencyProperty ObservedWidthProperty = DependencyProperty.RegisterAttached(
"ObservedWidth",
typeof(double),
typeof(SizeObserver));
public static readonly DependencyProperty ObservedHeightProperty = DependencyProperty.RegisterAttached(
"ObservedHeight",
typeof(double),
typeof(SizeObserver));
public static bool GetObserve(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (bool)frameworkElement.GetValue(ObserveProperty);
}
public static void SetObserve(FrameworkElement frameworkElement, bool observe)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObserveProperty, observe);
}
public static double GetObservedWidth(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (double)frameworkElement.GetValue(ObservedWidthProperty);
}
public static void SetObservedWidth(FrameworkElement frameworkElement, double observedWidth)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObservedWidthProperty, observedWidth);
}
public static double GetObservedHeight(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (double)frameworkElement.GetValue(ObservedHeightProperty);
}
public static void SetObservedHeight(FrameworkElement frameworkElement, double observedHeight)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObservedHeightProperty, observedHeight);
}
private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var frameworkElement = (FrameworkElement)dependencyObject;
if ((bool)e.NewValue)
{
frameworkElement.SizeChanged += OnFrameworkElementSizeChanged;
UpdateObservedSizesForFrameworkElement(frameworkElement);
}
else
{
frameworkElement.SizeChanged -= OnFrameworkElementSizeChanged;
}
}
private static void OnFrameworkElementSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateObservedSizesForFrameworkElement((FrameworkElement)sender);
}
private static void UpdateObservedSizesForFrameworkElement(FrameworkElement frameworkElement)
{
// WPF 4.0 onwards
frameworkElement.SetCurrentValue(ObservedWidthProperty, frameworkElement.ActualWidth);
frameworkElement.SetCurrentValue(ObservedHeightProperty, frameworkElement.ActualHeight);
// WPF 3.5 and prior
////SetObservedWidth(frameworkElement, frameworkElement.ActualWidth);
////SetObservedHeight(frameworkElement, frameworkElement.ActualHeight);
}
}
Size
właściwość, łączącą wysokość i szerokość. Około. 50% mniej kodu.ActualSize
własności wFrameworkElement
. Jeśli chcesz mieć bezpośrednie powiązanie dołączonych właściwości, musisz utworzyć dwie właściwości, które zostaną powiązane odpowiednio zActualWidth
iActualHeight
.Używam uniwersalnego rozwiązania, które działa nie tylko z ActualWidth i ActualHeight, ale także z dowolnymi danymi, z którymi można się powiązać przynajmniej w trybie odczytu.
Znacznik wygląda następująco, pod warunkiem, że ViewportWidth i ViewportHeight są właściwościami modelu widoku
Oto kod źródłowy elementów niestandardowych
źródło
Target
właściwość musi być zapisywalna, nawet jeśli nie można jej zmieniać z zewnątrz: - /Jeśli ktoś jest zainteresowany, zakodowałem tutaj przybliżenie rozwiązania Kenta:
Możesz go używać w swoich aplikacjach. To dobrze działa. (Dzięki, Kent!)
źródło
Oto inne rozwiązanie tego „błędu”, o którym pisałem tutaj na blogu:
Powiązanie OneWayToSource dla właściwości zależności ReadOnly
Działa przy użyciu dwóch właściwości zależności: Listener i Mirror. Odbiornik jest powiązany OneWay z TargetProperty iw PropertyChangedCallback aktualizuje właściwość Mirror, która jest powiązana OneWayToSource z tym, co zostało określone w Binding. Nazywam to
PushBinding
i można go ustawić na dowolnej właściwości zależności tylko do odczytu, takiej jak taPobierz projekt demonstracyjny tutaj .
Zawiera kod źródłowy i krótkie przykładowe użycie lub odwiedź mój blog WPF, jeśli interesują Cię szczegóły implementacji.
Ostatnia uwaga, ponieważ .NET 4.0 jesteśmy jeszcze bardziej oddaleni od wbudowanej obsługi tego rozwiązania, ponieważ powiązanie OneWayToSource odczytuje wartość z powrotem ze źródła po jego zaktualizowaniu
źródło
Podoba mi się rozwiązanie Dmitrija Taszkinowa! Jednak spowodowało to awarię mojego VS w trybie projektowania. Dlatego dodałem wiersz do metody OnSourceChanged:
źródło
Myślę, że można to zrobić nieco prościej:
xaml:
cs:
źródło