2010-06-23 3 views
9

여러 DataTemplate 요소를 기반으로 생성 된 양식이 있습니다. DataTemplate을 요소 중 하나는 다음과 같습니다 클래스에서 텍스트 상자를 만듭니다WPF 바인딩 및 동적으로 StringFormat 속성 할당

public class MyTextBoxClass 
{ 
    public object Value { get;set;} 
    //other properties left out for brevity's sake 
    public string FormatString { get;set;} 
} 

내가 방법을 "결합"바인딩의 "있는 StringFormat"속성에 정의한 formatString 속성의 값이 필요합니다. 지금까지 내가 가지고 :

<DataTemplate DataType="{x:Type vm:MyTextBoxClass}"> 
<TextBox Text="{Binding Path=Value, StringFormat={Binding Path=FormatString}" /> 
</DataTemplate> 

을하지만,있는 StringFormat는 종속성 속성이 아니므로, 나는 그것을 바인딩 할 수 없습니다.

내 다음 생각은 값 변환기를 만들고 ConverterParameter에 FormatString 속성 값을 전달하는 것이지만 동일한 문제가 발생했습니다. ConverterParameter는 DependencyProperty가 아닙니다.

그래서 지금 내가 너에게로 향했다. 바인딩의 StringFormat을 어떻게 동적으로 설정합니까? 더 구체적으로, TextBox?

코드 숨김을 사용하지 않아도되도록 XAML에서 작업을 수행하도록하는 것이 좋습니다. MVVM 패턴을 사용하고 있으며 뷰 모델과 뷰 사이의 경계를 가능한 한 흐려지지 않도록 유지하려고합니다.

감사합니다.

답변

2

TextBox을 상속하는 클래스를 만드는 방법과 해당 클래스에서 StringFormat을 위임 한 자체 종속성 속성을 만드는 방법이 있습니다. 따라서 XAML에서 TextBox을 사용하는 대신 상속 된 텍스트 상자를 사용하고 바인딩에서 고유 한 종속성 속성을 설정합니다.

+1

. 나는 이것을 조사해야 할 것이다. 나는 사용자 정의 컨트롤을 포함하지 않는 솔루션이있을 것이라고 기대했지만, 분명히 열려 있습니다. 나는 약간의 연구 끝에 다시 확인해 볼 것입니다. –

+0

나는 똑같은 일을하려하지만,이를 처리하기 위해 첨부 된 속성을 설정하는 방법을 모르겠습니다. 나는 새로운 질문을 게시했다. http://stackoverflow.com/q/24119097/65461 –

1

MyTextBoxClass.Value 대신 MyTextBoxClass 인스턴스에 텍스트 상자를 바인딩하고 valueconverter를 사용하여 값과 형식 문자열에서 문자열을 만듭니다.

또 다른 해결책은 가치와은 formatString 모두에 결합하는 것 다중 컨버터를 사용하는 것입니다.

첫 번째 솔루션은 속성의 변경을 지원하지 않습니다. 즉 값 또는 형식 스트링이 변경되면 다중 변환기를 사용하고 속성에 직접 바인딩하는 것처럼 변환기가 호출되지 않습니다.

+0

MyTextBoxClass 인스턴스에 바인딩하는 것은 내가 시도한 것이지만, 많은, 많은 속성이 있기 때문에 ValueConverter의 ConvertBack 메서드는 문제가 될 것이다. TextBox 객체에 대한 장소가 없다는 것입니다. 그래서 TextBox에서 불완전한 객체를 다시 가져옵니다. 다중 값 변환기를 살펴 보겠습니다. 그러나 FormatString은 종속성 속성이므로 바인딩 할 수 없으므로 작동하지 않을지 확실하지 않습니다. –

+0

어떻게 작동하나요? TextBox가 databinding을 사용하여 업데이트되면 텍스트는 FormatString을 사용하여 형식이 지정됩니다. 사용자가 텍스트 상자를 업데이트하면 FormatString의 형식과 일치하지 않는 텍스트를 입력 할 수 있습니다. 그 확인은? 대신 마스크 된 텍스트 상자를 사용하지 않으시겠습니까? 또한 FormatString은 다른 모든 공용 속성과 마찬가지로 바인딩 가능합니다. –

+0

"FormatString은 다른 공용 속성과 마찬가지로 바인딩 가능합니다"라는 이유를 설명하는 이유는 " '바인딩'은 '바인딩'속성의 'StringFormat'속성에서 설정할 수 없다는 오류가 발생합니다. '바인딩'은 DependencyObject의 DependencyProperty에 설정하십시오. " – jpierson

1

지정된 FormatString이있는 바인딩으로 바꿀 수있는 연결 동작을 만들 수 있습니다. FormatString 종속성 속성이 있으면 바인딩이 다시 한 번 업데이트됩니다. 바인딩이 업데이트되면 해당 바인딩에 FormatString이 다시 적용됩니다.

당신이 처리해야 할 것이라고 생각할 수있는 유일한 까다로운 두 가지. 한 가지 문제는 FormatString에 대해 서로 조화를 이루는 두 개의 연결된 속성을 만들고 FormatString을 적용해야하는 바인딩이있는 TargetProperty (예 : TextBox.Text)를 만들지 아니면 단순히 어떤 속성을 처리할지 가정 할 수 있는지 여부입니다 목표 제어 유형에 따라 달라집니다. 다른 문제는 기존 바인딩을 복사하고 사용자 정의 바인딩을 포함 할 수있는 다양한 유형의 바인딩이있는 경우 약간 수정하는 것일 수도 있습니다.

이 모든 것은 데이터에서 사용자 제어 방향으로 만 형식을 지정하기 때문에 중요합니다.늘어나는만큼 내가 MultiValue 같은 사용자 지정 MultiValueConverter 함께 원래 값과 FormatString 소비하고 원하는 출력을 생산 여전히 같은 ConvertBack 메서드는 출력 문자열이 주어지기 때문에 동일한 문제가 겪고있는 MultiBinding 같은 것을 사용하여 발견 할 수 있습니다. 그 시점에서 FormatString과 원래의 값을 거의 항상 읽을 수 없도록 해독해야합니다.

다음과 같은 것이다 양방향 서식 및 언 포맷팅 (unformatting)을 위해 일해야하는 남은 해결책 :

  • 는 야콥 크리스텐슨과 같은 원하는 형식 동작은 제안했다 텍스트 상자를 확장하는 사용자 지정 컨트롤을 작성합니다.
  • DependencyObject 또는 FrameworkElement에서 파생되고 FormatString DependencyProperty가있는 사용자 지정 값 변환기를 작성합니다. DependencyObject 경로를 이동하려면 OneWayToSource 바인딩을 "가상 분기"기술을 사용하여 FormatString 속성에 값을 넣을 수 있다고 생각합니다. 더 쉬운 다른 방법은 대신 FrameworkElement에서 상속 받고 다른 컨트롤과 함께 값 변환기를 비주얼 트리에 배치하여 ElementName에 필요할 때 바인더 만 바인딩 할 수 있습니다.
  • 이 게시물의 맨 위에서 언급 한 것과 유사한 동작을 사용하십시오. 대신 FormatString을 설정하는 대신 사용자 지정 값 변환기와 값 변환기에 전달되는 매개 변수에 대한 두 개의 연결된 속성이 있습니다 . 그런 다음 원래 바인딩을 수정하여 FormatString을 추가하는 대신 바인딩에 변환기와 변환기 매개 변수를 추가합니다. 개인적으로이 옵션을 사용하면 첨부 된 비헤이비어가 TextBox 이외의 다양한 상황에서 사용하기에 더 깨끗하지만 여전히 유연한 경향이 있기 때문에 가장 읽기 쉽고 직관적 인 결과가 될 것이라고 생각합니다. (DefaultValueConverter.cs @ referencesource.microsoft.com에서 영감)
2

이 코드는 동안에는은 formatString 다시 변환 할 수있는 상태에서 소스 속성 있으며, toString() 버전을 떠날 때, 텍스트 상자 또는 유사한 컨트롤에 바인딩 두 가지 방법을 사용할 수 있습니다. "1,234.56"을 구문 분석 할 수 있기 때문에 "#, 0.00"과 같은 형식이 좋지만 FormatString = "일부 접두사 텍스트 #, 0.00"은 다시 구문 분석 할 수없는 "일부 접두어 텍스트 1,234.56"으로 변환됩니다.

XAML : 소스 속성이 null이 될 수있는 경우

<TextBox> 
    <TextBox.Text> 
     <MultiBinding Converter="{StaticResource ToStringFormatConverter}" 
       ValidatesOnDataErrors="True" NotifyOnValidationError="True" TargetNullValue=""> 
      <Binding Path="Property" TargetNullValue="" /> 
      <Binding Path="PropertyStringFormat" Mode="OneWay" /> 
     </MultiBinding> 
    </TextBox.Text> 
</TextBox> 

참고 TargetNullValue 중복.

C 번호 :

좋은 제안입니다
/// <summary> 
/// Allow a binding where the StringFormat is also bound to a property (and can vary). 
/// </summary> 
public class ToStringFormatConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (values.Length == 1) 
      return System.Convert.ChangeType(values[0], targetType, culture); 
     if (values.Length >= 2 && values[0] is IFormattable) 
      return (values[0] as IFormattable).ToString((string)values[1], culture); 
     return null; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     var targetType = targetTypes[0]; 
     var nullableUnderlyingType = Nullable.GetUnderlyingType(targetType); 
     if (nullableUnderlyingType != null) { 
      if (value == null) 
       return new[] { (object)null }; 
      targetType = nullableUnderlyingType; 
     } 
     try { 
      object parsedValue = ToStringFormatConverter.TryParse(value, targetType, culture); 
      return parsedValue != DependencyProperty.UnsetValue 
       ? new[] { parsedValue } 
       : new[] { System.Convert.ChangeType(value, targetType, culture) }; 
     } catch { 
      return null; 
     } 
    } 

    // Some types have Parse methods that are more successful than their type converters at converting strings 
    private static object TryParse(object value, Type targetType, CultureInfo culture) 
    { 
     object result = DependencyProperty.UnsetValue; 
     string stringValue = value as string; 

     if (stringValue != null) { 
      try { 
       MethodInfo mi; 
       if (culture != null 
        && (mi = targetType.GetMethod("Parse", 
         BindingFlags.Public | BindingFlags.Static, null, 
         new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider) }, null)) 
        != null) { 
        result = mi.Invoke(null, new object[] { stringValue, NumberStyles.Any, culture }); 
       } 
       else if (culture != null 
        && (mi = targetType.GetMethod("Parse", 
         BindingFlags.Public | BindingFlags.Static, null, 
         new[] { typeof(string), typeof(IFormatProvider) }, null)) 
        != null) { 
        result = mi.Invoke(null, new object[] { stringValue, culture }); 
       } 
       else if ((mi = targetType.GetMethod("Parse", 
         BindingFlags.Public | BindingFlags.Static, null, 
         new[] { typeof(string) }, null)) 
        != null) { 
        result = mi.Invoke(null, new object[] { stringValue }); 
       } 
      } catch (TargetInvocationException) { 
      } 
     } 

     return result; 
    } 
}