2017-02-07 5 views
0

私は、Universal Windows Platformを使用してWindows Phone 10アプリケーションを構築しています。私のアプリでは、標準のCalendarViewを使用して、イベントのある日付の濃度を表示したいと考えています。このアイデアは、ページがロードされるとすぐにカレンダーをロードし、APIリクエストを行い、データ検索が成功するとCalendarViewがUIをリフレッシュしてCalendarViewDayItemChangingイベントが呼び出されるようにします。そこから、イベントを持つセルのための私の密度の色を設定することができます。コマンドのUWP CalendarViewアップデート

私は、1つの部分を除いてすべて正常に動作しています。カレンダーが最初に読み込まれるとき、私は一ヶ月に一ヶ月しか見ることができないように、現在の月の最小/最大の日付範囲を設定します。これにより、予定表のUIが期待どおりに更新されます。ただし、同じ日付にもう一度最小/最大の日付範囲を設定しようとすると、APIリクエストが完了した後、カレンダーでUIが更新されません。このため、CalendarViewに強制的にUIを更新させる方法はありません。

UpdateLayoutを呼び出そうとしましたが、最小/最大の日付範囲をリセットしようとしましたが、カレンダーのDataContextをデータの更新時に更新されたObservableCollectionにバインドしようとしました。これは動作せず、UIを更新するだけの方法はありません。

私はUWPにはかなり新しいので、私が間違っていることが分かりません。私はデータバインディングの概念がUWPの大きな部分であることは承知していますが、私のデータをこのCalendarViewにバインドして、データをリフレッシュするときにリフレッシュする方法がわかりません。助言がありますか?

以下は、私のコードを簡単に抜粋したものです。

XAML

<CalendarView 
    Name="Calendar" 
    NumberOfWeeksInView="6" 
    CalendarViewDayItemChanging="CalendarView_DayItemChanging" 
    DataContext="{Binding CalendarDates}"> 
</CalendarView> 

コードビハインド

namespace Pages 
{ 
    public sealed partial class CalendarPage : BasePage 
    { 
     #region Private Variables 

     private CalendarPageModel PageModel = new CalendarPageModel(); 
     private ObservableCollection<DateTime> CalendarDates; 

     #endregion 

     #region Constructor 

     public CalendarPage() 
     { 
      this.InitializeComponent(); 
      CalendarDates = new ObservableCollection<DateTime>(); 
     } 

     #endregion 

     #region Events 

     private void Page_Loaded(object sender, RoutedEventArgs args) 
     { 
      SetCalendarDateRange(); //NOTE: This is done here so that my UI consistantly shows the correct dates on the screen 
      LoadData(); 
     } 

     private void CalendarView_DayItemChanging(CalendarView sender, CalendarViewDayItemChangingEventArgs args) 
     { 
      if (!PageModel.DateHasEvent(args.Item.Date)) 
      { 
       args.Item.SetDensityColors(null); 
      } 
      else 
      { 
       List<Color> colors = new List<Color>(); 
       Color? color = Application.Current.Resources["CalendarHasEventDensityColor"] as Color?; 
       if (color != null) 
       { 
        colors.Add((Color)color); 
       } 

       args.Item.SetDensityColors(colors); 
      } 
     } 

     #endregion 

     #region Data 

     private void SetCalendarDateRange() 
     { 
      Calendar.MinDate = PageModel.StartDate; 
      Calendar.MaxDate = PageModel.EndDate; 
     } 

     private async void LoadData() 
     { 
      // get data 
      await PageModel.RefreshData(PageModel.StartDate, PageModel.EndDate); 

      // force calendar to update 
      //NOTE: This only works if the date range is actually different than what it's currently set to 
      SetCalendarDateRange(); 

      //NOTE: I have tried to just manually add a date to my observable collection to see if it'll kick off the calendar refresh, but it doesn't 
      CalendarDates.add(DateTime.Now); 
     } 

     #endregion 
    } 
} 
+0

MinDateとMaxDateを変更しても完全な再現(どこに 'CalendarPageModel'?)がないので、あなたのコードに問題がどこにあるのかわからないので、カレンダーにUIを更新させることは間違いありません。 'RefreshData'ではどうなりますか?それ以外は 'LoadData'を呼びますか? –

+0

それは重要ではありません。唯一の質問は、最小/最大の日付を変更せずにカレンダーを更新する方法です。 loadDataとrefreshDataの呼び出しがデータを取得し、そのデータをCalendarPageModelクラスに格納すると仮定できます。今すぐこのデータはCalendarViewに接続されていません。あなたが望むなら、私はそのコードを削除することができます。それにもかかわらず、質問はどのようにリフレッシュするためにCalendarViewを使用するのですか?上で概説したメンターを呼び出すことを意味し、最小/最大の日付は変更しません。 –

+0

途中でRuppeのために申し訳ありません。これを電話で入力します。 –

答えて

0

悪いニュース
CalendarView制御は、残念ながら、このシナリオのために設計されていません。多数の日数を表示するときのパフォーマンスが最適化されているため、個々の日がロードされたときにのみUIがリフレッシュされます。しかし

...

朗報
この動作を作成するために、コントロールを変更することが可能であるが、それは仕事の少しかかります。

基本的な原則は、Densityカラーブロックを描画し、バインディングを介して更新できるものにバインドすることです。この作業の例として

、ページに

<Page.Resources> 
    <local:ColorBrushConverter x:Key="BrushConverter" /> 
</Page.Resources> 
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 
    <StackPanel> 
     <CalendarView Name="Calendar" 
         DisplayMode="Month" 
         CalendarViewDayItemChanging="CalendarView_DayItemChanging" 
         > 
      <CalendarView.CalendarViewDayItemStyle> 
       <Style TargetType="CalendarViewDayItem" > 
        <Setter Property="Template"> 
         <Setter.Value> 
          <ControlTemplate TargetType="CalendarViewDayItem"> 
           <Grid Opacity="0.5"> 
            <Grid.RowDefinitions> 
             <RowDefinition Height="*"/> 
             <RowDefinition Height="*"/> 
             <RowDefinition Height="*"/> 
             <RowDefinition Height="*"/> 
             <RowDefinition Height="*"/> 
            </Grid.RowDefinitions> 
            <Rectangle Grid.Row="0" Fill="{Binding FifthColor, Converter={StaticResource BrushConverter}}" /> 
            <Rectangle Grid.Row="1" Fill="{Binding FourthColor, Converter={StaticResource BrushConverter}}" /> 
            <Rectangle Grid.Row="2" Fill="{Binding ThirdColor, Converter={StaticResource BrushConverter}}" /> 
            <Rectangle Grid.Row="3" Fill="{Binding SecondColor, Converter={StaticResource BrushConverter}}" /> 
            <Rectangle Grid.Row="4" Fill="{Binding FirstColor, Converter={StaticResource BrushConverter}}" /> 
           </Grid> 
          </ControlTemplate> 
         </Setter.Value> 
        </Setter> 
       </Style> 
      </CalendarView.CalendarViewDayItemStyle> 
     </CalendarView> 
     <Button Click="AddEventClicked">Add random event</Button> 
    </StackPanel> 
</Grid> 

そして、背後にあるコードに付随する次の行を追加します。

public sealed partial class MainPage : Page 
{ 
    private MyViewModel ViewModel; 

    private DateTime today; 
    private DateTime minDate; 
    private DateTimeOffset maxDate; 

    public MainPage() 
    { 
     this.InitializeComponent(); 

     // Keep these for reference 
     this.today = DateTime.Now.Date; 
     this.minDate = new DateTime(today.Year, today.Month, 1); 
     this.maxDate = minDate.AddMonths(1); 

     // Create our viewmodel 
     ViewModel = MyViewModel.Generate(minDate.Date, maxDate.Date); 
     Calendar.MinDate = minDate; 
     Calendar.MaxDate = maxDate; 

     // Add data for the next three days - will be shown when page loads 
     ViewModel.Dates[today.AddDays(1)].Add(Colors.Red); 
     ViewModel.Dates[today.AddDays(2)].Add(Colors.Purple); 
     ViewModel.Dates[today.AddDays(2)].Add(Colors.Blue); 
     ViewModel.Dates[today.AddDays(3)].Add(Colors.Green); 
    } 

    private void CalendarView_DayItemChanging(CalendarView sender, CalendarViewDayItemChangingEventArgs args) 
    { 
     // When the DayItem in the calendar is loaded 
     var itemDate = args?.Item?.Date.Date ?? DateTime.MinValue; 

     if (ViewModel.Dates.ContainsKey(itemDate)) 
     { 
      // Set the datacontext for our custom control 
      // - Which does support 2way binding :) 
      args.Item.DataContext = ViewModel.Dates[itemDate]; 
     } 
    } 

    private void AddEventClicked(object sender, RoutedEventArgs e) 
    { 
     var rand = new Random(); 
     var randomColor = Color.FromArgb(
             255, 
             (byte) rand.Next(0, 254), 
             (byte)rand.Next(0, 254), 
             (byte)rand.Next(0, 254)); 

     var randomDay = rand.Next(1, 29); 
     var randDateInMonth = new DateTime(today.Year, today.Month, randomDay); 

     if (ViewModel.Dates.ContainsKey(randDateInMonth)) 
     { 
      ViewModel.Dates[randDateInMonth].Add(randomColor); 
     } 
    } 
} 

public class MyViewModel 
{ 
    // The VM really just holds this dictionary 
    public Dictionary<DateTime, DensityColors> Dates { get; } 

    private MyViewModel() 
    { 
     this.Dates = new Dictionary<DateTime, DensityColors>(); 
    } 

    // Static constructor to limit the dates and populate dictionary 
    public static MyViewModel Generate(DateTime minDate, DateTime maxDate) 
    { 
     var generated = new MyViewModel(); 

     for (var i = 0; i < (maxDate - minDate).TotalDays; i++) 
     { 
      generated.Dates.Add(minDate.AddDays(i), new DensityColors()); 
     } 

     return generated; 
    } 
} 

public class DensityColors : ObservableCollection<Color>, INotifyPropertyChanged 
{ 
    // Properties that expose items in underlying OC 
    public Color FirstColor => Items.Any() ? Items.First() : Colors.Transparent; 
    public Color SecondColor => Items.Count > 1 ? Items.Skip(1).First() : Colors.Transparent; 
    public Color ThirdColor => Items.Count > 2 ? Items.Skip(2).First() : Colors.Transparent; 
    public Color FourthColor => Items.Count > 3 ? Items.Skip(3).First() : Colors.Transparent; 
    public Color FifthColor => Items.Count > 4 ? Items.Skip(4).First() : Colors.Transparent; 

    protected override void InsertItem(int index, Color item) 
    { 
     base.InsertItem(index, item); 

     // Hacky forcing of updating UI for all properties 
     OnPropertyChanged(nameof(FirstColor)); 
     OnPropertyChanged(nameof(SecondColor)); 
     OnPropertyChanged(nameof(ThirdColor)); 
     OnPropertyChanged(nameof(FourthColor)); 
     OnPropertyChanged(nameof(FifthColor)); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

public class ColorBrushConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, string language) 
    { 
     if (value is Color) 
     { 
      return new SolidColorBrush((Color)value); 
     } 

     return value; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, string language) 
    { 
     throw new NotImplementedException(); 
    } 
} 

これは、一日あたり5つのエントリ(ない建てのように10に制限されていますコントロールでは、これ以上は無視されます)、必要に応じて後でやっていることを達成する方法や、必要に応じて修正する方法を知っておくべきです。

+0

私はマットを助けてくれてありがとう。これは実現可能な解決策のように見えるので、受け入れられた答えとしてマークします。しかし、私はちょうど私の問題を解決する方法をハッキングしました。基本的には、最初のページの読み込み時にカレンダーを非表示にして、最初にデータを読み込んだ後、今月の日付範囲を強制してカレンダーを表示します。それ以降、日付範囲が更新されたときにのみデータが更新され、CalendarViewDayItemChangingが有効になり、それに基づいて更新が可能になります。少しハッキーですが、それは動作します。 –

関連する問題