天天看點

【C#】wpf自定義calendar日期選擇控件的樣式首先上圖,看下樣式原理實作

原文: 【C#】wpf自定義calendar日期選擇控件的樣式

首先上圖,看下樣式

【C#】wpf自定義calendar日期選擇控件的樣式首先上圖,看下樣式原理實作
【C#】wpf自定義calendar日期選擇控件的樣式首先上圖,看下樣式原理實作

1. 總覽:

Calendar本質上是一個6x7的清單,這個清單可以用ItemsControl來實作。其填充的每一個item都是一個自定義樣式的Button,Button外面是一個圓形的border。根據Button按鈕的IsEnabled狀态,設定foreground以達到灰色不點選的效果。

2. ItemsControl内容的生成:

主要是想辦法生成第一行第一列的日期,其他的日期隻需要DateTime.AddDays(i)即可。代碼參考:

private void SetCalendar(int year, int month)
{
            _days.Clear();//請忽略
            DateTime datetime = new DateTime(year, month, 1);
            int week = (int)datetime.DayOfWeek;//擷取指定月份的1号是周幾
            datetime = datetime.AddDays(1 - week);
            for (int i = 0; i < 42; i++)
            {
            _days.Add(datetime.AddDays(i));
            }
            OnPropertyChanged("Days");//請忽略
}           

首先,将目前年月傳入,擷取到目前月份的第一天是周幾,然後推算出ItemsControl的第一行第一列的Datetime,最後就是順次相加即可。

1. 界面的實作

先上代碼:

<Grid  Grid.RowSpan="4" Margin="0,54,0,0" Visibility="{Binding IsShowCalendar,Converter={StaticResource BoolToVisibilityCollapseConverter}}" Background="White" Width="350" HorizontalAlignment="Right">
                <Grid.RowDefinitions>
                    <RowDefinition Height="50"></RowDefinition>
                    <RowDefinition Height="auto"></RowDefinition>
                    <RowDefinition Height="auto"></RowDefinition>
                    <RowDefinition Height="*"></RowDefinition>
                </Grid.RowDefinitions>
                <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
                    <Button VerticalAlignment="Center"  Command="{Binding YearCommand}" CommandParameter="Pre" Style="{StaticResource TransparentButtonStyle}">
                        <Button.Content>
                            <TextBlock Text="&lt;" FontSize="20"></TextBlock>
                        </Button.Content>
                    </Button>
                    <TextBlock VerticalAlignment="Center" FontSize="15" Text="{Binding CurrentYear}"></TextBlock>
                    <Button VerticalAlignment="Center"  Command="{Binding YearCommand}" CommandParameter="Next" Style="{StaticResource TransparentButtonStyle}">
                        <Button.Content>
                            <TextBlock Text="&gt;" FontSize="20"></TextBlock>
                        </Button.Content>
                    </Button>
                    <Button Margin="50,10,10,10" VerticalAlignment="Center" Command="{Binding MonthCommand}" CommandParameter="Pre" Style="{StaticResource TransparentButtonStyle}">
                        <Button.Content>
                            <TextBlock Text="&lt;" FontSize="20"></TextBlock>
                        </Button.Content>
                    </Button>
                    <TextBlock VerticalAlignment="Center" Text="{Binding CurrentMonth}" FontSize="15"></TextBlock>
                    <Button VerticalAlignment="Center" Command="{Binding MonthCommand}" CommandParameter="Next" Style="{StaticResource TransparentButtonStyle}">
                        <Button.Content>
                            <TextBlock Text="&gt;" FontSize="20"></TextBlock>
                        </Button.Content>
                    </Button>
                </StackPanel>
                <UniformGrid Grid.Row="1" Columns="7">
                    <TextBlock Text="MON" TextAlignment="Center"></TextBlock>
                    <TextBlock Text="TUE" TextAlignment="Center"></TextBlock>
                    <TextBlock Text="WED" TextAlignment="Center"></TextBlock>
                    <TextBlock Text="THU" TextAlignment="Center"></TextBlock>
                    <TextBlock Text="FRI" TextAlignment="Center"></TextBlock>
                    <TextBlock Text="SAT" TextAlignment="Center"></TextBlock>
                    <TextBlock Text="SUN" TextAlignment="Center"></TextBlock>
                </UniformGrid>
                <ItemsControl Grid.Row="2" ScrollViewer.HorizontalScrollBarVisibility="Auto"
                ScrollViewer.VerticalScrollBarVisibility="Auto"
                            ItemsSource="{Binding Days}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Button Background="{Binding Converter={StaticResource BACKUP_DateTimeToColorConverter}}"  Style="{StaticResource CalendarTransparentButtonStyle}"
                                    Content="{Binding Converter={StaticResource BACKUP_DateTimeToDayConverter}}" 
                                    Command="{Binding DataContext.ChooseDateCommand,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Window}}}" CommandParameter="{Binding}"
                                    MouseDoubleClick="Button_MouseDoubleClick">
                                <Button.Foreground>
                                    <MultiBinding Converter="{StaticResource BACKUP_DateTimeToForegroundMultiConverter}">
                                        <Binding></Binding>
                                        <Binding Path="DataContext.CurrentMonth" RelativeSource="{RelativeSource FindAncestor,AncestorType={x:Type Window}}"></Binding>
                                    </MultiBinding>
                                </Button.Foreground>
                                <Button.IsEnabled>
                                    <MultiBinding Converter="{StaticResource BACKUP_DateTimeToEnableMultiConverter}">
                                        <Binding></Binding>
                                        <Binding Path="DataContext.CutoffDayBegin" RelativeSource="{RelativeSource FindAncestor,AncestorType={x:Type Window}}"></Binding>
                                        <Binding Path="DataContext.CutoffDayEnd" RelativeSource="{RelativeSource FindAncestor,AncestorType={x:Type Window}}"></Binding>
                                    </MultiBinding>
                                </Button.IsEnabled>
                            </Button>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <UniformGrid Columns="7"></UniformGrid>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
                <StackPanel Grid.Row="3" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,31" Orientation="Horizontal">
                    <Button Content="Cancel" Style="{StaticResource btnSecondaryStyle}" Grid.Row="2" Command="{Binding HideCalendarCommand}"></Button>
                    <Button Content="{Binding CalendarSetText}" Style="{StaticResource btnPrimaryStyle}" Grid.Row="2" Command="{Binding HideCalendarCommand}"></Button>
                </StackPanel>
            </Grid>           

其中,大量用到了Binding和MVVM,如果對方面還不是很紮實,可以參考下

劉鐵錳的視訊

.

這段代碼中,主要部分是ItemsControl的ItemsSource綁定。這裡綁定的是Days,其類型是

public ObservableCollection<DateTime> _days = new ObservableCollection<DateTime>();
        public ObservableCollection<DateTime> Days
        {
            get
            {
                return _days;
            }
        }           

在設定Button的前景色和IsEnabled狀态的時候,你也看到了用的是MultiBinding,并定義了兩個Converter:

1. DateTimeToForegroundMultiConverter

class DateTimeToForegroundMultiConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            DateTime dt=new DateTime();
            if(values[0] is DateTime)
            {
                dt = (DateTime)values[0];
            }
            int currentMonth;
            Int32.TryParse(values[1].ToString(), out currentMonth);
            if (dt.Month == currentMonth)
            {
                if(dt.ToShortDateString()==DateTime.Now.ToShortDateString())
                {
                    return new SolidColorBrush(Colors.White);
                }
                else
                {
                    return new SolidColorBrush(Colors.Black);
                }
            }
            else
            {
                return new SolidColorBrush(Colors.Gray);
            }
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }           

2. DateTimeToEnableMultiConverter

class DateTimeToEnableMultiConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {

            DateTime currentDay, cutoffDayBegin,cutoffDayEnd;
            if(values[0] is DateTime&&values[1] is DateTime&&values[2] is DateTime)
            {
                currentDay = (DateTime)values[0];
                cutoffDayBegin = (DateTime)values[1];
                cutoffDayEnd = (DateTime)values[2];
                if (DateTime.Compare(currentDay, cutoffDayBegin) >= 0 && DateTime.Compare(currentDay, cutoffDayEnd) <= 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }


        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }           

2. 背景ViewModel的實作

其實背景沒有什麼代碼,有一個SetCalendar方法,在月份或者年份發生變動的時候調用一下即可。

private void SetCalendar(int year, int month)
        {
            _days.Clear();
            DateTime datetime = new DateTime(year, month, 1);
            int week = (int)datetime.DayOfWeek;
            datetime = datetime.AddDays(1 - week);
            for (int i = 0; i < 42; i++)
            {

                _days.Add(datetime.AddDays(i));
            }
            OnPropertyChanged("Days");
        }           

還有一個就是Button點選之後的Command指令:

private void exeChooseDate(object obj)
        {
            DateTime dt;
            DateTime.TryParse(obj.ToString(), out dt);
            CurrentMonth = dt.Month;
            CurrentYear = dt.Year;
        }           

Button每點選一次,就會執行一次這個方法,CurrentMonth和CurrentYear這兩個屬性的值就會被更改,一些來決定是否需要重新繪制UI切換的上一個/下一個月份。

CurrentMonth和CurrentYear屬性見下:

private int _currentYear = 2010;
        public int CurrentYear
        {
            get
            {
                return _currentYear;
            }
            set
            {
                if (_currentYear != value && value > 1978 && value < 9999)
                {
                    _currentYear = value;
                    OnPropertyChanged("CurrentYear");
                    SetCalendar(_currentYear, CurrentMonth);
                }
            }
        }

        private int _currentMonth = 1;
        public int CurrentMonth
        {
            get
            {
                return _currentMonth;
            }
            set
            {
                if (_currentMonth != value && value < 13 && value > 0)
                {
                    _currentMonth = value;
                    OnPropertyChanged("CurrentMonth");
                    SetCalendar(CurrentYear, _currentMonth);
                }
            }
        }