none
请问WPF中的日历控件(Calendar)怎么做数据绑定? RRS feed

  • 问题

  • 我自己研究了一段时间,发现Calendar没有ItemsSource属性,不知道如何做绑定。。。

    我们的日程数据在服务器上,我会将其下载下来,然后希望将这些数据与Calendar控件关联起来,这样我就可以在日历控件的模版里面根据需求自定义显示外观了。

    我曾经尝试着在CalendarDayButtonStyle的模版里面,通过Converter来设置按钮的外观,但是我马上发现,当Converter运行的时候,我需要从服务器下载数据,用的是异步方式,这样一来无法将结果返回给Converter...如果换成同步方式,似乎降低了用户体验感。。。

    并且,当数据在本地被修改之后,也无法反映到Calendar控件上。所以还是希望研究如何做数据绑定,请高人指点!



    da jia hao!

    2017年10月4日 4:00

答案

  • Hi,

    >>动态绑定是什么意思?

    手动后台绑定。

    >> 我现在用的是Converter,但是我发现只要控件一加载完成,就会自动触发Converter,不受我控制。

    不要使用UI线程执行下载操作,尝试开启另一个线程来下载数据。
    >>我无法做到在日历控件加载之前去下载好数据,并且用户还可能会点击切换到下一个月,此时也需要去下载新的月份的数据(我不可能一次性把所有数据都下载下来吧?),此时只能够在Converter里面用同步的方法去下载数据了。

    当用户切换下一个月时,如果上一个下载操作的线程还没返回数据,你应该限制用户进行切换或者终止上一个下载操作线程。

    >>(我不可能一次性把所有数据都下载下来吧?),此时只能够在Converter里面用同步的方法去下载数据了。

    如果用户切换很频繁,下载操作岂不是太频繁, 网络很慢的时候,同步方法岂不是会导致阻塞线程。

    >> 这样做了之后,导致我的日程数据也需要存放在Converter类中,并且要设置为静态变量。。。这明显不是优雅的设计。。。

    你的问题更适合讨论, 因为答案是发散的,取决于你如何设计你的程序。

    个人认为,如果用户的日程数据变动的不是很频繁,你完全没有必要每次刷新数据,你可以把数据缓存到本地。

    你也可以使用定时器定时的从数据库获取数据。 你也可以使用数据库的表触发器,当数据库的日程数据发生变化时,发生通知给你的程序,通知你的程序更新数据。

    http://blog.csdn.net/smartsmile2012/article/details/53704227

    http://blog.csdn.net/cnm_1314/article/details/52605073

    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • 已标记为答案 liubin 2017年10月10日 3:19
    2017年10月9日 8:47
    版主

全部回复

  • Hi,

    >>我自己研究了一段时间,发现Calendar没有ItemsSource属性,不知道如何做绑定.

    你可以将一个Calendar控件的SelectedDate属性(SelectionMode 是 SingleDate时),绑定到可空的DateTime类型。

        <Calendar SelectionMode="SingleDate" SelectedDate="{Binding selectDate}"/>

    SelectedDates属性是只读的,SelectionMode 为 SingleRangeMultipleRange时所以您无法绑定到SelectedDates属性。如果想要将Calendar控件的一组当前选定日期存储在变量中,则可以处理日历控件的SelectedDatesChanged事件, 此事件当用户选择或取消选择日历控件中的日期时调用。

    public DateTime[] selectedDates{ get; set; }
     
    private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
    {
        System.Windows.Controls.Calendar cal = (System.Windows.Controls.Calendar)sender;
     
        selectedDates= cal.SelectedDates.ToArray();
    }

    Calendar控件没有ItemsSource属性, 从你的描述来看,你是需要绑定一个事件集合,这里恐怕你不能使用绑定。

    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    2017年10月6日 2:04
    版主
  • 实际需求是这样:

    日程安排的数据在服务器上,我们需要将其显示在一个日历控件中。

    我会先去下载数据,然后再想办法显示在日历控件里面。

    我到stackoverflow查过资料,发现可以在CalendarDayButton的模版中用Converter来做,即在Converter中去下载数据,然后判断日程安排的日期是否和当前日期相同,如果是就将日程安排显示出来。但是我发现这样做了之后,经常会导致在XAML的设计窗口中出现卡死的问题(即无法显示窗体设计),我怀疑可能是因为在设计状态下,设计器也会按照Converter中的代码去下载数据,但由于此时我尚未登录,所以它下载不成功,导致卡死?

    我是否不应该将下载数据这个操作放在Converter中呢?

    但是如果不放在converter中,那到底应该放在什么地方比较合适?


    da jia hao!


    • 已编辑 liubin 2017年10月6日 7:59
    2017年10月6日 7:57
  • Hi,

    如果你要下载数据,你应该用多线程来做,在数据下载完成之后在进行绑定,可以尝试使用BackgroundWorker。

           public DateTime[] selectedDates { get; set; }
    ...
                BackgroundWorker backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += BackgroundWorker_DoWork;
                backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
    ...
            private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
            {
              //下载操作
    
              //填充属性
            }
    
            private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                //动态绑定
            }

    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    2017年10月6日 8:10
    版主
  • 动态绑定是什么意思?我现在用的是Converter,但是我发现只要控件一加载完成,就会自动触发Converter,不受我控制。

    我无法做到在日历控件加载之前去下载好数据,并且用户还可能会点击切换到下一个月,此时也需要去下载新的月份的数据(我不可能一次性把所有数据都下载下来吧?),此时只能够在Converter里面用同步的方法去下载数据了。

    这样做了之后,导致我的日程数据也需要存放在Converter类中,并且要设置为静态变量。。。这明显不是优雅的设计。。。

    下面是日历按钮的样式:

            <Style x:Key="CalendarDayButtonStyle" TargetType="{x:Type CalendarDayButton}">
                <Setter Property="FontFamily" Value="Verdana"></Setter>
                <Setter Property="MinWidth" Value="28" />
                <Setter Property="MinHeight" Value="5" />
                <Setter Property="FontSize" Value="14"></Setter>
                <Setter Property="HorizontalContentAlignment" Value="Center" />
                <Setter Property="VerticalContentAlignment" Value="Center" />
                <Setter Property="Background" Value="Transparent" />
                <Setter Property="Margin" Value="0" />
                <Setter Property="IsTabStop" Value="False" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type CalendarDayButton}">
                            <Grid x:Name="Grid" Margin="{TemplateBinding Margin}">
                                <Rectangle x:Name="TodayBackground" IsHitTestVisible="False" Fill="Transparent" Stroke="#ff9966" StrokeThickness="2" Visibility="Hidden"/>
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"></RowDefinition>
                                        <RowDefinition></RowDefinition>
                                        <RowDefinition></RowDefinition>
                                    </Grid.RowDefinitions>
                                    <Border x:Name="Bg" Background="{TemplateBinding Background}"/>
                                    <TextBlock Text="{Binding StringFormat={}{0:dd}}" Foreground="{Binding Converter={StaticResource SpecialDayConverter}}" VerticalAlignment="Top" HorizontalAlignment="Center"></TextBlock>
                                    <!--显示农历-->
                                    <TextBlock Text="{Binding Converter={StaticResource ChineseDayConverter}}" Foreground="#aaa" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" FontSize="12" Grid.Row="1" x:Name="LunisolarDayTextBlock" HorizontalAlignment="Center"></TextBlock>
                                    <!--有日程安排的日子-->
                                    <TextBlock Text="{Binding Converter={StaticResource AppointmentConverter}}" Background="OrangeRed" Foreground="White" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" FontSize="12" Grid.Row="2" x:Name="ScheduledDayTextBlock" HorizontalAlignment="Center"></TextBlock>
                                </Grid>
                            </Grid>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsSelected" Value="True">
                                    <Setter Property="Background" Value="#f2f2f2" TargetName="Grid"></Setter>
                                    <Setter Property="Foreground" Value="#0078D7"></Setter>
                                </Trigger>
                                <Trigger Property="IsToday" Value="True">
                                    <Setter Property="FontWeight" Value="Bold"></Setter>
                                    <Setter Property="Visibility" Value="Visible" TargetName="TodayBackground"></Setter>
                                </Trigger>
                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsMouseOver" Value="True"></Condition>
                                        <Condition Property="IsSelected" Value="False"></Condition>
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Background" TargetName="Grid" Value="#cfe7fd"></Setter>
                                </MultiTrigger>
                                <!--不可用日期-->
                                <Trigger Property="IsBlackedOut" Value="True">
                                    <Setter Property="Opacity" Value="0.5" TargetName="Grid"></Setter>
                                    <Setter Property="Foreground" Value="Green"></Setter>
                                </Trigger>
                                <!--不在当月的日期-->
                                <Trigger Property="IsInactive" Value="True">
                                    <Setter Property="Opacity" Value="0.5" TargetName="Grid"></Setter>
                                </Trigger>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter Property="Opacity" Value="0.5" TargetName="Grid"></Setter>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    

    其中“AppointmentConverter”就是承担了去服务器下载数据,然后返回有日程安排的数据给日历按钮的。



    da jia hao!

    2017年10月8日 3:17
  • 我发现在Converter里面去下载数据,在非调试状态下(即编写C#代码、修改XAML代码的时候)会导致VS频繁出现报错、卡死:

    这个问题到底要如何才能够解决?帮帮我。。。


    da jia hao!

    2017年10月9日 3:31
  • Hi,

    >>动态绑定是什么意思?

    手动后台绑定。

    >> 我现在用的是Converter,但是我发现只要控件一加载完成,就会自动触发Converter,不受我控制。

    不要使用UI线程执行下载操作,尝试开启另一个线程来下载数据。
    >>我无法做到在日历控件加载之前去下载好数据,并且用户还可能会点击切换到下一个月,此时也需要去下载新的月份的数据(我不可能一次性把所有数据都下载下来吧?),此时只能够在Converter里面用同步的方法去下载数据了。

    当用户切换下一个月时,如果上一个下载操作的线程还没返回数据,你应该限制用户进行切换或者终止上一个下载操作线程。

    >>(我不可能一次性把所有数据都下载下来吧?),此时只能够在Converter里面用同步的方法去下载数据了。

    如果用户切换很频繁,下载操作岂不是太频繁, 网络很慢的时候,同步方法岂不是会导致阻塞线程。

    >> 这样做了之后,导致我的日程数据也需要存放在Converter类中,并且要设置为静态变量。。。这明显不是优雅的设计。。。

    你的问题更适合讨论, 因为答案是发散的,取决于你如何设计你的程序。

    个人认为,如果用户的日程数据变动的不是很频繁,你完全没有必要每次刷新数据,你可以把数据缓存到本地。

    你也可以使用定时器定时的从数据库获取数据。 你也可以使用数据库的表触发器,当数据库的日程数据发生变化时,发生通知给你的程序,通知你的程序更新数据。

    http://blog.csdn.net/smartsmile2012/article/details/53704227

    http://blog.csdn.net/cnm_1314/article/details/52605073

    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • 已标记为答案 liubin 2017年10月10日 3:19
    2017年10月9日 8:47
    版主