읽기 전용 GUI 속성을 ViewModel로 되돌리는 중
View에서 일부 읽기 전용 종속성 속성의 현재 상태를 항상 알고 있는 ViewModel을 작성하려고 합니다.
특히 GUI에는 FlowDocument에서 한 번에 한 페이지씩 표시되는 FlowDocumentPageViewer가 포함되어 있습니다.FlowDocumentPageViewer는 CanGoToPreviousPage와 CanGoToNextPage라는2개의 읽기 전용 의존관계 속성을 표시합니다.View Model이 이 두 View 속성의 값을 항상 알고 있어야 합니다.
OneWayToSource 데이터 바인딩으로 이 작업을 수행할 수 있습니다.
<FlowDocumentPageViewer
CanGoToNextPage="{Binding NextPageAvailable, Mode=OneWayToSource}" ...>
이것이 허용된다면 완벽할 것입니다. FlowDocumentPageViewer의 CanGoToNextPage속성이 변경될 때마다 ViewModel의 NextPageAvailable속성에 새로운 값이 푸시됩니다.이것이 바로 제가 원하는 것입니다.
유감스럽게도, 이것은 컴파일 되지 않습니다.'CanGoToPreviousPage' 속성이 읽기 전용이며 마크업에서 설정할 수 없다는 오류가 나타납니다.읽기 전용 속성은 어떤 종류의 데이터 바인딩도 지원하지 않습니다. 해당 속성과 관련하여 읽기 전용인 데이터 바인딩도 지원하지 않습니다.
ViewModel의 속성을 DependencyProperties로 하고 OneWay 바인딩을 반대로 할 수는 있지만 관심사 분리 위반에는 관심이 없습니다(ViewModel은 View에 대한 참조가 필요하며, 이는 MVVM 데이터 바인딩이 방지되어야 합니다).
FlowDocumentPageViewer는 CanGoToNextPageChanged 이벤트를 공개하지 않습니다.또한 DependencyProperty에서 변경 알림을 받을 수 있는 좋은 방법은 알 수 없습니다.이러한 방법은 바인드할 다른 DependencyProperty를 작성하는 것 외에는 알 수 없습니다.
뷰의 읽기 전용 속성 변경 내용을 ViewModel에 계속 알려주려면 어떻게 해야 합니까?
네, 제가 예전에 이 일을 했던 적이 있어요.ActualWidth
그리고.ActualHeight
둘 다 읽기 전용 속성입니다.내가 만든 첨부 동작은ObservedWidth
그리고.ObservedHeight
첨부 속성.또,Observe
초기 후크업 실행에 사용되는 속성.사용 방법은 다음과 같습니다.
<UserControl ...
SizeObserver.Observe="True"
SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}"
뷰 모델에는Width
그리고.Height
항상 와 동기화되는 속성ObservedWidth
그리고.ObservedHeight
첨부 속성.그Observe
재산은 단순히 에 붙어 있다SizeChanged
의 FrameworkElement
에서는 . 핸핸음음, 음음음 its its its its its its its its its its its its its its its its its its its its its its ObservedWidth
★★★★★★★★★★★★★★★★★」ObservedHeight
에르고,Width
★★★★★★★★★★★★★★★★★」Height
의 경우 의뷰 is is of 、 、 of 、 of 、 of of of of of of of of 。ActualWidth
★★★★★★★★★★★★★★★★★」ActualHeight
UserControl
.
아마도 완벽한 솔루션이 아닐 수도 있습니다(동의합니다. 읽기 전용 DP는OneWayToSource
MVVM을 사용하다 그 명,는,ObservedWidth
★★★★★★★★★★★★★★★★★」ObservedHeight
DP는 읽기 전용이 아닙니다.
업데이트: 위에서 설명한 기능을 구현하는 코드는 다음과 같습니다.
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);
}
}
저는 ExperialWidth와 ExperialHeight뿐만 아니라 적어도 읽기 모드에서 바인딩할 수 있는 모든 데이터와 연동되는 범용 솔루션을 사용하고 있습니다.
마크업은 ViewportWidth 및 Viewport를 지정하면 다음과 같습니다.높이는 뷰 모델의 특성입니다.
<Canvas>
<u:DataPiping.DataPipes>
<u:DataPipeCollection>
<u:DataPipe Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualWidth}"
Target="{Binding Path=ViewportWidth, Mode=OneWayToSource}"/>
<u:DataPipe Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualHeight}"
Target="{Binding Path=ViewportHeight, Mode=OneWayToSource}"/>
</u:DataPipeCollection>
</u:DataPiping.DataPipes>
<Canvas>
커스텀 요소의 소스 코드는 다음과 같습니다.
public class DataPiping
{
#region DataPipes (Attached DependencyProperty)
public static readonly DependencyProperty DataPipesProperty =
DependencyProperty.RegisterAttached("DataPipes",
typeof(DataPipeCollection),
typeof(DataPiping),
new UIPropertyMetadata(null));
public static void SetDataPipes(DependencyObject o, DataPipeCollection value)
{
o.SetValue(DataPipesProperty, value);
}
public static DataPipeCollection GetDataPipes(DependencyObject o)
{
return (DataPipeCollection)o.GetValue(DataPipesProperty);
}
#endregion
}
public class DataPipeCollection : FreezableCollection<DataPipe>
{
}
public class DataPipe : Freezable
{
#region Source (DependencyProperty)
public object Source
{
get { return (object)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(object), typeof(DataPipe),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnSourceChanged)));
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DataPipe)d).OnSourceChanged(e);
}
protected virtual void OnSourceChanged(DependencyPropertyChangedEventArgs e)
{
Target = e.NewValue;
}
#endregion
#region Target (DependencyProperty)
public object Target
{
get { return (object)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register("Target", typeof(object), typeof(DataPipe),
new FrameworkPropertyMetadata(null));
#endregion
protected override Freezable CreateInstanceCore()
{
return new DataPipe();
}
}
관심 있는 사람이 있다면, 켄트의 솔루션에 대한 근사치를 여기에 코드화해 두겠습니다.
class SizeObserver
{
#region " Observe "
public static bool GetObserve(FrameworkElement elem)
{
return (bool)elem.GetValue(ObserveProperty);
}
public static void SetObserve(
FrameworkElement elem, bool value)
{
elem.SetValue(ObserveProperty, value);
}
public static readonly DependencyProperty ObserveProperty =
DependencyProperty.RegisterAttached("Observe", typeof(bool), typeof(SizeObserver),
new UIPropertyMetadata(false, OnObserveChanged));
static void OnObserveChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
FrameworkElement elem = depObj as FrameworkElement;
if (elem == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
elem.SizeChanged += OnSizeChanged;
else
elem.SizeChanged -= OnSizeChanged;
}
static void OnSizeChanged(object sender, RoutedEventArgs e)
{
if (!Object.ReferenceEquals(sender, e.OriginalSource))
return;
FrameworkElement elem = e.OriginalSource as FrameworkElement;
if (elem != null)
{
SetObservedWidth(elem, elem.ActualWidth);
SetObservedHeight(elem, elem.ActualHeight);
}
}
#endregion
#region " ObservedWidth "
public static double GetObservedWidth(DependencyObject obj)
{
return (double)obj.GetValue(ObservedWidthProperty);
}
public static void SetObservedWidth(DependencyObject obj, double value)
{
obj.SetValue(ObservedWidthProperty, value);
}
// Using a DependencyProperty as the backing store for ObservedWidth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ObservedWidthProperty =
DependencyProperty.RegisterAttached("ObservedWidth", typeof(double), typeof(SizeObserver), new UIPropertyMetadata(0.0));
#endregion
#region " ObservedHeight "
public static double GetObservedHeight(DependencyObject obj)
{
return (double)obj.GetValue(ObservedHeightProperty);
}
public static void SetObservedHeight(DependencyObject obj, double value)
{
obj.SetValue(ObservedHeightProperty, value);
}
// Using a DependencyProperty as the backing store for ObservedHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ObservedHeightProperty =
DependencyProperty.RegisterAttached("ObservedHeight", typeof(double), typeof(SizeObserver), new UIPropertyMetadata(0.0));
#endregion
}
앱에서 자유롭게 사용하세요.잘 돼. (고마워 켄트!)
여기에서는, 이 「버그」에 대한 또 다른 대처법을 블로그에 게재하고 있습니다.
Dependency
'청취자 거울'입니다.Listener one OneWay 、 TargetProperty 、 Property Changed Callback 、 OneWayToSource mirror listener listener listener listener listener listener listener listener listener listener listener listener listener listener listener listener listener 。는 그것을 나는그 it it라고 부른다.PushBinding
, 이러한 의 의존 할 수 .
<TextBlock Name="myTextBlock"
Background="LightBlue">
<pb:PushBindingManager.PushBindings>
<pb:PushBinding TargetProperty="ActualHeight" Path="Height"/>
<pb:PushBinding TargetProperty="ActualWidth" Path="Width"/>
</pb:PushBindingManager.PushBindings>
</TextBlock>
여기에서 데모 프로젝트를 다운로드하십시오.
여기에는 소스 코드와 짧은 샘플 사용이 포함되어 있습니다.
마지막 메모는 이후입니다.NET 4.0은 OneWayToSource 바인딩을 업데이트한 후 소스로부터 값을 읽어내기 때문에 이 기능의 빌트인 지원과는 거리가 멀어져 있습니다.
드미트리 타슈키노프의 솔루션이 마음에 들어요!하지만 디자인 모드에서 VS가 충돌했습니다.그래서 OnSource Changed 메서드에 행을 추가했습니다.
Private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){(!(부울)Designer Properties(디자이너 속성)IsInDesignModeProperty 입니다.GetMetadata(type of(DependencyObject)).디폴트값)((DataPipe)d).OnSource Changed(e); }
좀 더 간단하게 할 수 있을 것 같아요.
xaml:
behavior:ReadOnlyPropertyToModelBindingBehavior.ReadOnlyDependencyProperty="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"
behavior:ReadOnlyPropertyToModelBindingBehavior.ModelProperty="{Binding MyViewModelProperty}"
cs:
public class ReadOnlyPropertyToModelBindingBehavior
{
public static readonly DependencyProperty ReadOnlyDependencyPropertyProperty = DependencyProperty.RegisterAttached(
"ReadOnlyDependencyProperty",
typeof(object),
typeof(ReadOnlyPropertyToModelBindingBehavior),
new PropertyMetadata(OnReadOnlyDependencyPropertyPropertyChanged));
public static void SetReadOnlyDependencyProperty(DependencyObject element, object value)
{
element.SetValue(ReadOnlyDependencyPropertyProperty, value);
}
public static object GetReadOnlyDependencyProperty(DependencyObject element)
{
return element.GetValue(ReadOnlyDependencyPropertyProperty);
}
private static void OnReadOnlyDependencyPropertyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
SetModelProperty(obj, e.NewValue);
}
public static readonly DependencyProperty ModelPropertyProperty = DependencyProperty.RegisterAttached(
"ModelProperty",
typeof(object),
typeof(ReadOnlyPropertyToModelBindingBehavior),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static void SetModelProperty(DependencyObject element, object value)
{
element.SetValue(ModelPropertyProperty, value);
}
public static object GetModelProperty(DependencyObject element)
{
return element.GetValue(ModelPropertyProperty);
}
}
언급URL : https://stackoverflow.com/questions/1083224/pushing-read-only-gui-properties-back-into-viewmodel
'programing' 카테고리의 다른 글
Swift에서 지연을 발생시키는 방법은 무엇입니까? (0) | 2023.04.13 |
---|---|
팬더와 엑셀 스타일 데이트 변환 (0) | 2023.04.13 |
Intelij 터미널에서 git 설정 (0) | 2023.04.13 |
WPF 창의 시작 위치 변경 (0) | 2023.04.13 |
EPPlus와 MemoryStream의 사용 (0) | 2023.04.13 |