القيمة المحسوبة لربط بيانات WPF أحيانًا دقائق وأحيانًا ساعات

StackOverflow https://stackoverflow.com/questions/1066727

سؤال

لدي نافذة WPF مع مربع نص، وذلك باستخدام ربط بيانات WPF القياسي لكائن ما.كل هذا يعمل بشكل جيد، المشكلة هي:

يقوم المستخدم بإدخال تقدير زمني، وأود أن أمنح المستخدم خيار إدخال البيانات بالساعات أو الدقائق (من خلال القائمة المنسدلة).أرغب في الاحتفاظ بالبيانات في دقائق، وإذا اختار المستخدم الساعات، فاضرب مدخلاته في 60 وقم بتخزين البيانات.

كيف يمكنني تحقيق ذلك باستخدام ربط بيانات WPF؟هل هذا ممكن حتى؟

يحرر

على سبيل المثال، ستظهر 90 دقيقة كـ 1.5 إذا تم تعيين القائمة المنسدلة على ساعات، ولكن 90 إذا تم تحديد دقائق.

هل كانت مفيدة؟

المحلول

يمكنك استخدام خاصية خاصة على نافذتك:

<ComboBox x:Name="Units">
    <sys:String>Hours</sys:String>
    <sys:String>Minutes</sys:String>
</ComboBox>
<TextBox x:Name="Value" Text="{Binding Path=Estimate, ElementName=ThisWindow}" />

ثم قم بتنفيذ الخاصية الخاصة:

public double Estimate
{
    get
    {
        switch(this.Units.SelectedItem as String)
        {
        case "Hours":
            return this.estimate / 60.0;
        default:
            return this.estimate;
        }
    }

    set
    {
        switch(this.Units.SelectedItem as String)
        {
        case "Hours":
            this.estimate = value * 60.0;
            break;
        default:
            this.estimate = value;
            break;
        }

        this.OnPropertyChanged("Estimate");
    }
}

نصائح أخرى

بعد سوء فهم السؤال تمامًا في البداية، إليك مجهودي للحصول على مكافأتي الأولى؛)

<Window.Resources>        
    <local:DivisionConverter x:Key="HoursConverter" Divisor="60"/>

    <DataTemplate DataType="{x:Type local:MyObject}">
        <StackPanel Orientation="Horizontal">
            <TextBox x:Name="InputBox" Text="{Binding Path=Estimate}" Width="80"/>
            <ComboBox x:Name="InputUnit" SelectedItem="Minutes">
                <System:String>Minutes</System:String>
                <System:String>Hours</System:String>
            </ComboBox>
        </StackPanel>            
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding ElementName=InputUnit, Path=SelectedItem}" Value="Hours">
                <Setter TargetName="InputBox" Property="Text" Value="{Binding Path=Estimate, Converter={StaticResource HoursConverter}}"/>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>        
</Window.Resources>
<Grid>
    <ContentControl VerticalAlignment="Top">
        <local:MyObject Estimate="120"/>
    </ContentControl>
</Grid>

بشكل عام لا أحب المحولات المتخصصة:بعد فترة من الوقت، تفقد المسار الذي يقوم به المحول بالضبط، وينتهي بك الأمر إلى المرور عبر كود المحول في كل مرة تعتقد أنك بحاجة إلى محول، أو الأسوأ من ذلك أنك تقوم بإنشاء محول ثانٍ يقوم بنفس الشيء تمامًا.لذا فأنا الآن أستخدمها فقط عندما يفشل كل شيء آخر.

بدلا من ذلك قمت بتحديد غرض عام DivisionConverter, ، الذي يقسم القيمة في طريقة التحويل، ويضرب القيمة في طريقة ConvertBack، تمامًا كما تتوقع، لا داعي لإلقاء نظرة على الكود هنا ;)

بالإضافة إلى ذلك، أستخدم DataTemplate وTriggers حيث استخدم الآخرون MultiBinding أو (الأسوأ من imo) أضافوا منطق واجهة المستخدم هذا في getter وsetter.يؤدي هذا إلى الاحتفاظ بكل منطق واجهة المستخدم البسيط نسبيًا في مكان واحد يمكن الإشراف عليه.

الآن فقط لكي نكتمل، هذا هو الكود الخلفي:

public class MyObject
{
    public double Estimate { get; set; }
}


public class DivisionConverter : IValueConverter
{
    public double Divisor { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double doubleValue = (double)value;
        return doubleValue / Divisor;            
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {           
        double inputValue;
        if (!double.TryParse((string)value, NumberStyles.Any, culture, out inputValue)) 
            return 0;

        return inputValue * Divisor;
    }
}

يمكنك استخدام MultiValueConverter:

XAML

        <ComboBox ItemsSource="{StaticResource values}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock>
                      <TextBlock.Text>
                        <MultiBinding Converter="{StaticResource minutToHours}">
                          <Binding/>
                          <Binding Path="IsChecked" ElementName="chkHours"/>
                        </MultiBinding>
                      </TextBlock.Text>
                    </TextBlock>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
        <CheckBox Name="chkHours" Content="Show hours"/>

كود C# للمحول

public class MinuteToHoursConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int minutes = 0;
        if (values[0] is TimeSpan)
        {
            minutes = (int)((TimeSpan)values[0]).TotalMinutes;
        }
        else
        {
            minutes = System.Convert.ToInt32(values[0]);
        }
        bool showHours = System.Convert.ToBoolean(values[1]);
        if (showHours)
            return (minutes / 60.0).ToString();
        else
            return minutes.ToString();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }

    #endregion
}

لكن هذا الحل ربما ليس الأمثل..IMHO أفضل طريقة إذا كنت تستخدم نمط MVVM هي إنشاء خاصية إضافية في ViewModel لحساب القيمة الصحيحة

ربما أفكر كثيرًا في هذا، ولكن بما أن مربع التحرير والسرد يحتوي على قيمة عرض وقيمة فعلية (خلف الكواليس من المستخدم)، فلماذا لا تحتوي قائمة التقديرات الخاصة بك على عمودين...كل من الساعات والدقائق على التوالي.بعد ذلك، بناءً على الوضع الذي تم تحديده، ما عليك سوى تغيير رابط "العرض" إلى الوضع المعني.بهذه الطريقة، سيتم دائمًا تخزين عدد محاضر ما وراء الكواليس الذي تريده.لا يوجد تحويل آخر من/إلى.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top