Сохранение данных в пользовательском элементе управления - PullRequest
0 голосов
/ 14 января 2012

Это простой элемент управления датой и временем, в который добавлена ​​функция минут и часов.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace foo.WizardElements
{

/// <summary>
/// Interaction logic for DateTimeRangeElement.xaml
/// </summary>
public partial class DateTimeRangeElement : UserControl
{
    public DateTimeRangeElement()
    {
        InitializeComponent();
        dp.DataContext = this;
    }

    private void Clear_Click(object sender, RoutedEventArgs e)
    {
        Date = null;
        Hours = 0;
        Minutes = 0;
    }
    public static readonly DependencyProperty DateProperty = DependencyProperty.Register("Date",
                                                                                        typeof(DateTime?),
                                                                                        typeof(DateTimeRangeElement));

    public DateTime? Date
    {
        get { return (DateTime?)GetValue(DateProperty); }
        set 
        { 
            SetValue(DateProperty, value);
        }
    }

    public static readonly DependencyProperty HoursProperty = DependencyProperty.Register("Hours",
                                                                                typeof(int),
                                                                                typeof(DateTimeRangeElement));

    public int Hours
    {
        get { return (int)GetValue(HoursProperty); }
        set
        {
            SetValue(HoursProperty, value);
        }
    }

    public static readonly DependencyProperty MinutesProperty = DependencyProperty.Register("Minutes",
                                                                                typeof(int),
                                                                                typeof(DateTimeRangeElement));

    public int Minutes
    {
        get { return (int)GetValue(MinutesProperty); }
        set
        {
            SetValue(MinutesProperty, value);
        }
    }

    private void TimeUpdated()
    {
        if(Hours > 23)
            Hours = 23;
        if(Minutes > 59)
            Minutes = 59;
        if (Date.HasValue && (Date.Value.Hour != Hours || Date.Value.Minute != Minutes))
        {
            Date = new DateTime(Date.Value.Year, Date.Value.Month, Date.Value.Day, Hours, Minutes, 0);
        }
        if ((!Date.HasValue) && (Hours > 0 || Minutes > 0))
        {
            var now = DateTime.Now;
            Date = new DateTime(now.Year, now.Month, now.Day, Hours, Minutes, 0);
        }
    }

    private void Changed(object sender, object e)
    {
        TimeUpdated();
    }

}
}

и xaml

<UserControl x:Class="foo.WizardElements.DateTimeRangeElement"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
             xmlns:behavior="clr-namespace:foo.Behaviours"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="myDateTimeControl">
    <Grid>
        <StackPanel Orientation="Horizontal" Margin="2" Height="24">
            <DatePicker x:Name="dp" 
                        VerticalAlignment="top" 
                        Foreground="LightGray" 
                        SelectedDate="{Binding ElementName=myDateTimeControl,Path=Date, Mode=TwoWay}" 
                        BorderBrush="{x:Null}" SelectedDateChanged="Changed"
                        >
                <DatePicker.Background>
                    <SolidColorBrush Color="white" Opacity="0.2"/>
                </DatePicker.Background>
            </DatePicker>
            <TextBox Width="30"  Text="{Binding ElementName=myDateTimeControl, Path=Hours, Mode=TwoWay, StringFormat=00}" TextChanged="Changed">
                <i:Interaction.Behaviors>
                    <behavior:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]*$" MaxLength="3" />
                </i:Interaction.Behaviors>
            </TextBox>
            <Label Content=":"/>

            <TextBox Width="30" Text="{Binding ElementName=myDateTimeControl, Path=Minutes, Mode=TwoWay, StringFormat=00}" TextChanged="Changed">
                <i:Interaction.Behaviors>
                    <behavior:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]*$" MaxLength="3" />
                </i:Interaction.Behaviors>
            </TextBox>
            <Grid  HorizontalAlignment="Left" VerticalAlignment="top">
                <Button 
                    Background="Transparent" 
                    Click="Clear_Click" 
                    BorderThickness="0"
                    >
                    <Grid>
                        <Image Source="/foo;component/Images/Clear-left.png" Stretch="Uniform"/>
                    </Grid>
                </Button>
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>

Итак, вот пример использования, вы положили этого щенкана вкладке выберите время и дату, отойдите от вкладки и вернитесь.Если вы связываете данные только со свойством «Дата», а не с часами и минутами, вы обнаружите, что оба из них установлены на 0.

Причина этого в том, что после перехода с вкладки вы избавляетесь отпользовательский контроль, и когда вы возвращаетесь назад, вы приветствуете новый пользовательский контроль (.Net 4).

Привязка к часам и минутам является бесполезной и не имеет смысла для потребителя, поскольку он ожидает объект DateTime.

Я пытаюсь выяснить, каков будет шаблон corect для повторной загрузки часов и минут при воссоздании usercontrol.

это то, как usercontrol используется в приложении

<we:DateTimeRangeElement Date="{Binding Path=Filter.StartTime, Mode=TwoWay}" />

РЕДАКТИРОВАТЬ: У меня есть решение, которое мне не нравится, но оно будет работать, пока я не смогу избавиться от клея.я создал свой объект datetime и использовал его, чтобы получить более привязываемый объект.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using ToolSuite.Contract.BaseClasses;
using System.Globalization;

namespace foo.WizardElements
{
    /// <summary>
    /// Interaction logic for DateTimeRangeElement.xaml
    /// </summary>
    public partial class DateTimeRangeElement : UserControl
    {
        public DateTimeRangeElement()
        {
            InitializeComponent();
            this.GotFocus += DateTimeRangeElement_GotFocus;
        }

        void DateTimeRangeElement_GotFocus(object sender, RoutedEventArgs e)
        {
            if(Date!=null)
                Date.PropertyChanged += Date_PropertyChanged;
        }

        void Date_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.GetBindingExpression(DateProperty).UpdateSource();
        }

        private void Clear_Click(object sender, RoutedEventArgs e)
        {
            Date = null;
        }
        public static readonly DependencyProperty DateProperty = DependencyProperty.Register("Date",
                                                                                            typeof(FriendlyDateTime),
                                                                                            typeof(DateTimeRangeElement));

        public FriendlyDateTime Date
        {
            get { return (FriendlyDateTime)GetValue(DateProperty); }
            set 
            {
                SetValue(DateProperty, value);
            }
        }
    }
}

xaml

    <UserControl x:Class="foo.WizardElements.DateTimeRangeElement"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
             xmlns:behavior="clr-namespace:foo.Behaviours"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="myDateTimeControl">
    <Grid>
        <StackPanel Orientation="Horizontal" Margin="2" Height="24">
            <DatePicker x:Name="dp" 
                        VerticalAlignment="top" 
                        Foreground="LightGray" 
                        SelectedDate="{Binding ElementName=myDateTimeControl,Path=Date.Date, Mode=TwoWay}" 
                        BorderBrush="{x:Null}"
                        >
                <DatePicker.Background>
                    <SolidColorBrush Color="white" Opacity="0.2"/>
                </DatePicker.Background>
            </DatePicker>
            <TextBox x:Name="Hours" Width="30"  Text="{Binding ElementName=myDateTimeControl, Path=Date.Hour, Mode=TwoWay, StringFormat=00}">
                <i:Interaction.Behaviors>
                    <behavior:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]*$" MaxLength="3" />
                </i:Interaction.Behaviors>
            </TextBox>
            <Label Content=":"/>

            <TextBox x:Name="Minutes" Width="30" Text="{Binding ElementName=myDateTimeControl, Path=Date.Minute, Mode=TwoWay, StringFormat=00}">
                <i:Interaction.Behaviors>
                    <behavior:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]*$" MaxLength="3" />
                </i:Interaction.Behaviors>
            </TextBox>
            <Grid  HorizontalAlignment="Left" VerticalAlignment="top">
                <Button 
                    Background="Transparent" 
                    Click="Clear_Click" 
                    BorderThickness="0"
                    >
                    <Grid>
                        <Image Source="/foo;component/Images/Clear-left.png" Stretch="Uniform"/>
                    </Grid>
                </Button>
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>

помощник

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ToolSuite.Contract.BaseClasses;

namespace foo.WizardElements
{
    public class FriendlyDateTime : NotifyPropertyChangedBase
    {
        public FriendlyDateTime()
        {

        }
        public FriendlyDateTime(DateTime? value)
        {
            Date = value;
        }

        public DateTime? Date
        {
            get
            {
                if (base._values.ContainsKey("Date"))
                    return Get<DateTime>("Date");
                else
                    return null;
            }

            set
            {
                if (value == null)
                {
                    if (base._values.ContainsKey("Date"))
                        base._values.Remove("Date");
                }
                else
                    Set<DateTime>("Date", value.Value);

                base.NotifyPropertyChanged("Date");
                base.NotifyPropertyChanged("Hour");
                base.NotifyPropertyChanged("Minute");
            }
        }
        public int Hour
        {
            get { return Date.HasValue ? Date.Value.Hour : 0; }
            set
            {
                if (Hour > 23)
                    Hour = 23;
                var d = Date.HasValue ? Date.Value : DateTime.Now;
                Date = new DateTime(d.Year, d.Month, d.Day, value, Minute, 0);
            }
        }
        public int Minute
        {
            get { return Date.HasValue ? Date.Value.Minute : 0; }
            set
            {
                if (Minute > 59)
                    Minute = 59;
                var d = Date.HasValue ? Date.Value : DateTime.Now;
                Date = new DateTime(d.Year, d.Month, d.Day, Hour, value, 0);
            }
        }

        static public implicit operator DateTime?(FriendlyDateTime value)
        {
            return value.Date;
        }

        static public implicit operator FriendlyDateTime(DateTime? value)
        {
            // Note that because RomanNumeral is declared as a struct, 
            // calling new on the struct merely calls the constructor 
            // rather than allocating an object on the heap:
            return new FriendlyDateTime(value);
        }
    }
}

несколько бесполезный четверг, от которого я хотел бы избавиться

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Globalization;
using ToolSuite.Contract.BaseClasses;

namespace foo.WizardElements
{
    public class FriendlyDateTimeValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (targetType == typeof(DateTime?))
            {
                return ((FriendlyDateTime)value).Date;
            }
            else if (targetType == typeof(FriendlyDateTime))
            {
                return new FriendlyDateTime(value as DateTime?);
            }
            return null;
        }



        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (targetType == typeof(DateTime?))
            {
                return ((FriendlyDateTime)value).Date;
            }
            else if (targetType == typeof(FriendlyDateTime))
            {
                return new FriendlyDateTime(value as DateTime?);
            }
            return null;
        }

    }
}

и, наконец, способ его использования

                <we:DateTimeRangeElement Date="{Binding Path=Filter.EndTime, Mode=TwoWay, Converter={StaticResource DateTimeConverter}, UpdateSourceTrigger=Explicit}" />

Мне это нравится?Нет, не очень хочется, чтобы я мог угробить конвертер значений и явную привязку.но это будет исследовательский проект на другой день.

Ответы [ 3 ]

2 голосов
/ 14 января 2012

Решение - шаблон MVVM.Ваши связанные данные не должны иметь никакого отношения к элементу управления.Даже если вы отойдете от вкладки и элементы управления содержанием будут удалены, данные останутся .

EDIT

Я вижу, что в XAML вы связываетев свойствах вашего пользовательского элемента управления.Это в корне неправильно.Вы должны использовать ваши данные из DataLayer, а не из элемента управления.Создайте класс, который будет использоваться как держатель значений для этих свойств.

0 голосов
/ 14 января 2012

Это зависит от того, является ли это универсальный UserControl, предназначенный для использования многими различными приложениями, или одноразовый UserControl, предназначенный для использования в одном конкретном случае.

Если это общий элемент управления, почему бы не связать Часы / Минуты со связанным свойством Date? Для этого просто свяжите с DateTimeRangeElement.Date.Hours (или Minutes) вместо DateTimeRangeElement.Hours. Если пользователь привязывает один объект данных даты, он будет ожидать, что часы / минуты этого объекта будут обновлены, когда они в любом случае изменят значения в вашем элементе управления.

Если вы не хотите этого делать, то пользователь может установить DataBind Hours / Minutes, если он хочет помешать сбросу значения. Это похоже на использование любого другого UserControl с TabControl - CheckBox.IsChecked, ListBox.SelectedItem, Expander.IsExpanded и т. Д. Все теряются, если они не связаны с чем-то.

Если это однократный UserControl, предназначенный для использования с определенным View и TabControl, который вы можете контролировать, то просто связывайте часы / минуты.

Другой альтернативой, которую я использовал в прошлом, является перезапись TabControl для кэширования ContentPresenter, когда вкладка выгружается, и использование кэшированного ContentPresenter вместо повторной загрузки элемента вкладки при переключении назад. Если вы хотите код для этого, он находится в этот ответ на другой вопрос

0 голосов
/ 14 января 2012

Вы связываете int hour и int min:

    public DateTime StartTime
    {
        get { return startdate; }
        set
        {
            startdate = new DateTime(value.Year, value.Month, value.Day, startdate.Hour, startdate.Minute, 0);
            RaisePropertyChanged("StartTime");
            RaisePropertyChanged("StartHour");
            RaisePropertyChanged("StartMinute");
        }
    }

    public int StartHour
    {
        get { return StartTime.Hour; }
        set
        {
            startdate = new DateTime(startdate.Year, startdate.Month, startdate.Day, value, startdate.Minute, 0);

        }
    }

то же самое в течение минуты .... По крайней мере, это то, что я делал раньше с использованием MVVM, но все это содержится в объекте данных в моей ViewModel.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...