2012-04-30 4 views
4

.Net 4.5의 지연 바인딩 태그를 사용하고 있지만 변경 내용이 "커밋"되지 않은 동안 텍스트 상자의 배경색을 변경하고 싶습니다. 지연이 발생하는 동안 어떻게 IsDirty 속성을 true로 설정할 수 있습니까?텍스트 상자가 수정되는시기를 아는 방법?

TextChanged 이벤트를 사용하여 IsDirty 플래그를 설정하고 바운드 속성이 설정된 경우 플래그를 제거해 보았습니다. 문제는 TextChanged가 바운드 속성이 변경 될 때마다 발생하며 사용자가 텍스트를 수정할 때가 아니라 발생한다는 것입니다.

TextChanged 이벤트와 바운드 속성을 모니터링하여 매우 까다 롭고 연약한 방식으로 "작동"시켰습니다. 말할 필요도없이 매우 버그가 발생하기 때문에 깨끗한 솔루션을 원합니다. 텍스트 상자가 변경되었지만 아직 지연되지 않았다는 것을 알 수있는 방법이 있습니까?

답변

5

소스 코드를 살펴본 결과 BindingExpressionBaseNeedsUpdate이라는 속성을 통해이 사실을 알고 있습니다. 그러나이 속성은 내부적이므로 리플렉션을 사용하여 가져와야합니다.

그러나 쉬운 방법으로는이 속성을 모니터링 할 수 없습니다. 따라서 내가 보는대로 TextChangedSourceUpdated 이벤트를 사용하여 NeedsUpdate이 변경되었을 때를 알아야합니다.

업데이트 나는이 작업을 수행 연결된 동작을 만들어, 어떤 DependencyProperty에 보류중인 업데이트를 모니터링하는 데 사용할 수 있습니다. NotifyOnSourceUpdated은 반드시으로 설정해야합니다.

업로드 여기에 작은 샘플 프로젝트 : PendingUpdateExample.zip

<TextBox Text="{Binding ElementName=textBoxSource, 
         Path=Text, 
         NotifyOnSourceUpdated=True, 
         UpdateSourceTrigger=PropertyChanged, 
         Delay=1000}" 
     ab:UpdatePendingBehavior.MonitorPendingUpdates="{x:Static TextBox.TextProperty}"> 
    <TextBox.Style> 
     <Style TargetType="TextBox"> 
      <Style.Triggers> 
       <Trigger Property="ab:UpdatePendingBehavior.HasPendingUpdates" 
         Value="True"> 
        <Setter Property="Background" Value="Green"/> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
    </TextBox.Style> 
</TextBox> 

UpdatePendingBehavior

public class UpdatePendingBehavior 
{ 
    #region MonitorPendingUpdates 

    public static DependencyProperty MonitorPendingUpdatesProperty = 
     DependencyProperty.RegisterAttached("MonitorPendingUpdates", 
              typeof(DependencyProperty), 
              typeof(UpdatePendingBehavior), 
              new UIPropertyMetadata(null, MonitorPendingUpdatesChanged)); 

    public static DependencyProperty GetMonitorPendingUpdates(FrameworkElement obj) 
    { 
     return (DependencyProperty)obj.GetValue(MonitorPendingUpdatesProperty); 
    } 
    public static void SetMonitorPendingUpdates(FrameworkElement obj, DependencyProperty value) 
    { 
     obj.SetValue(MonitorPendingUpdatesProperty, value); 
    } 

    public static void MonitorPendingUpdatesChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) 
    { 
     DependencyProperty property = e.NewValue as DependencyProperty; 
     if (property != null) 
     { 
      FrameworkElement element = target as FrameworkElement; 
      element.SourceUpdated += elementProperty_SourceUpdated; 

      if (element.IsLoaded == true) 
      { 
       SubscribeToChanges(element, property); 
      } 
      element.Loaded += delegate { SubscribeToChanges(element, property); }; 
      element.Unloaded += delegate { UnsubscribeToChanges(element, property); }; 
     } 
    } 

    private static void SubscribeToChanges(FrameworkElement element, DependencyProperty property) 
    { 
     DependencyPropertyDescriptor propertyDescriptor = 
      DependencyPropertyDescriptor.FromProperty(property, element.GetType()); 
     propertyDescriptor.AddValueChanged(element, elementProperty_TargetUpdated); 
    } 

    private static void UnsubscribeToChanges(FrameworkElement element, DependencyProperty property) 
    { 
     DependencyPropertyDescriptor propertyDescriptor = 
       DependencyPropertyDescriptor.FromProperty(property, element.GetType()); 
     propertyDescriptor.RemoveValueChanged(element, elementProperty_TargetUpdated); 
    } 

    private static void elementProperty_TargetUpdated(object sender, EventArgs e) 
    { 
     FrameworkElement element = sender as FrameworkElement; 
     UpdatePendingChanges(element); 
    } 

    private static void elementProperty_SourceUpdated(object sender, DataTransferEventArgs e) 
    { 
     FrameworkElement element = sender as FrameworkElement; 
     if (e.Property == GetMonitorPendingUpdates(element)) 
     { 
      UpdatePendingChanges(element); 
     } 
    } 

    private static void UpdatePendingChanges(FrameworkElement element) 
    { 
     BindingExpressionBase beb = BindingOperations.GetBindingExpressionBase(element, GetMonitorPendingUpdates(element)); 
     BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; 
     PropertyInfo needsUpdateProperty = beb.GetType().GetProperty("NeedsUpdate", bindingFlags); 
     SetHasPendingUpdates(element, (bool)needsUpdateProperty.GetValue(beb)); 
    } 

    #endregion // MonitorPendingUpdates 

    #region HasPendingUpdates 

    public static DependencyProperty HasPendingUpdatesProperty = 
     DependencyProperty.RegisterAttached("HasPendingUpdates", 
              typeof(bool), 
              typeof(UpdatePendingBehavior), 
              new UIPropertyMetadata(false)); 

    public static bool GetHasPendingUpdates(FrameworkElement obj) 
    { 
     return (bool)obj.GetValue(HasPendingUpdatesProperty); 
    } 
    public static void SetHasPendingUpdates(FrameworkElement obj, bool value) 
    { 
     obj.SetValue(HasPendingUpdatesProperty, value); 
    } 

    #endregion // HasPendingUpdates 
} 

또 다른 방법은 공동 소스와 대상 모두를 바인드하고 변환기에서 해당 값을 비교하는 MultiBinding을 사용해야합니다. 그런 다음 Style에있는 Background을 변경할 수 있습니다. 이 값은으로 변환하지 않는다고 가정합니다.이 TextBoxes

<TextBox Text="{Binding ElementName=textBoxSource, 
         Path=Text, 
         UpdateSourceTrigger=PropertyChanged, 
         Delay=2000}"> 
    <TextBox.Style> 
     <Style TargetType="TextBox"> 
      <Style.Triggers> 
       <DataTrigger Value="False"> 
        <DataTrigger.Binding> 
         <MultiBinding Converter="{StaticResource IsTextEqualConverter}"> 
          <Binding RelativeSource="{RelativeSource Self}" 
            Path="Text"/> 
          <Binding ElementName="textBoxSource" Path="Text"/> 
         </MultiBinding> 
        </DataTrigger.Binding> 
        <Setter Property="Background" Value="Green"/> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </TextBox.Style> 
</TextBox> 
<TextBox Name="textBoxSource"/> 

IsTextEqualConverter

public class IsTextEqualConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     return values[0].ToString() == values[1].ToString(); 
    } 
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotSupportedException(); 
    } 
} 
0

PreviewTextInput 또는 주요 관련 이벤트 중 하나를 시도해 볼 수 있습니다. (버블 링 이벤트가 내부적으로 처리 될 가능성이 있으므로 tunneling 버전이 필요합니다.)

+0

와 예는 텍스트 상자에 붙여 넣기를 잡을 수 없습니다. – Manuel

+0

@Manuel : 알겠습니다. 그게 문제가 될 것입니다 ... –

0

지연 바인딩에 대해 잘 모르겠습니다. 그러나, 닷넷 4.0에서 나는 BindingGroup을 사용할 것이다. BindingGroup에는 원하는대로 정확하게보고 할 수있는 CanRestoreValues ​​속성이 있습니다. UpdateSourceTrigger가 Explicit (BindingGroup이있는 경우 기본값) 인 경우 CanRestoreValues는 BindingGroup.CommitEdit가 호출 될 때까지 바인딩 된 컨트롤 값이 변경된 시점부터 true가됩니다. 값은 바인드 된 객체로 전달됩니다. 그러나 BindingGroup을 공유하는 모든 컨트롤이 보류 값을 가지면 CanRestoreValues가 true가됩니다. 따라서 각 별도의 속성에 대한 피드백을 원할 경우 컨트롤 당 하나의 BindingGroup을 사용해야하므로 CommitEdit를 약간 불편하게 만듭니다.

관련 문제