2017-02-07 2 views
0

Ich baue eine Windows Phone 10-Anwendung mit Universal Windows Platform. In meiner App habe ich eine Standard-Kalenderansicht, die Dichtewerte für Termine mit Ereignissen anzeigen möchte. Die Idee ist, den Kalender zu laden, sobald die Seite geladen ist, eine API-Anfrage zu machen, und nach erfolgreichem Datenabruf das CalendarView seine Benutzeroberfläche aktualisieren, so dass das CalendarViewDayItemChanging Ereignis aufgerufen wird. Von dort kann ich meine Dichte Farben für die Zellen, die Ereignisse haben.UWP CalendarView Update auf Befehl

Ich habe so ziemlich alles funktioniert richtig bis auf einen Teil. Wenn der Kalender zum ersten Mal geladen wird, setze ich die Min/Max-Datumsbereiche auf den aktuellen Monat, sodass nur jeweils ein Monat angezeigt wird. Dadurch wird die Benutzeroberfläche des Kalenders wie erwartet aktualisiert. Nachdem jedoch meine API-Anforderung abgeschlossen ist, wenn ich versuche, die Min/Max-Datumsbereiche erneut auf dieselben Daten festzulegen, aktualisiert der Kalender seine Benutzeroberfläche nicht. Aus diesem Grund kann ich CalendarView nicht zwingen, seine Benutzeroberfläche zu aktualisieren.

Ich habe versucht, UpdateLayout aufzurufen, habe versucht, die Min/Max-Datumsbereiche zurückzusetzen, und ich habe versucht, den DataContext des Kalenders an eine ObservableCollection in meinem Code zu binden, der aktualisiert wird, wenn meine Daten aktualisiert werden. Nichts davon funktioniert und ich sehe keine Methode, um nur die Benutzeroberfläche zu aktualisieren.

Ich bin ziemlich neu in UWP, bin also unsicher, was ich falsch mache. Ich weiß, dass das Konzept der Datenbindung ein großer Teil von UWP ist, aber ich bin mir nicht sicher, wie ich meine Daten an diese Kalenderansicht binden würde, damit sie aktualisiert wird, wenn meine Daten aktualisiert werden. Irgendwelche Vorschläge?

Unten finden Sie einen kurzen Auszug meines Codes, wie er jetzt steht.

XAML

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

-Code-behind

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

Es ist definitiv möglich, den Kalender die Benutzeroberfläche aktualisieren, wenn Sie die MinDate und MaxDate ändern, aber Sie haben keine vollständige Repro (wo ist "CalendarPageModel"?) So kann nicht sehen, wo das Problem in Ihrem Code ist. Was passiert in 'RefreshData'? Was nennt man noch 'LoadData'? –

+0

Nichts davon sollte von Bedeutung sein. Es ist abstrahiert, da die einzige Frage, die ich habe, ist, wie ich den Kalender aktualisieren kann, ohne die Min/Max-Daten zu ändern. Sie können einfach davon ausgehen, dass die Aufrufe loadData und refreshData Daten abrufen und die Daten in der CalendarPageModel-Klasse speichern. Momentan sind diese Daten nicht mit dem CalendarView verbunden. Ich könnte diesen Code entfernen, wenn Sie möchten. Wie auch immer, die Frage ist, wie Sie das CalendarView auffrischen, was bedeutet, den Mentor anzurufen, den ich oben skizziert habe, ohne die Min/Max-Daten zu ändern? –

+0

Sorry für die Ruppes übrigens. Tippen Sie das an einem Telefon ein. –

Antwort

0

Die schlechte Nachricht
Die CalendarView Steuerung leider nicht für dieses Szenario ausgelegt ist. Da die Anzeige bei der Anzeige einer großen Anzahl von Tagen auf Leistung optimiert ist, wird die Benutzeroberfläche nur aktualisiert, wenn ein einzelner Tag geladen wird.

jedoch ...

Die gute Nachricht
Es ist möglich, die Steuerung zu modifizieren, um dieses Verhalten zu erstellen, aber es dauert ein wenig Arbeit.

Das Grundprinzip besteht darin, die Verantwortung für das Zeichnen der Density-Farbblöcke zu übernehmen und sie an etwas zu binden, das durch Bindungen aktualisiert werden kann.

Als Beispiel für diese Arbeit, auf eine Seite, die folgende hinzufügen

<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> 

Und beigefügten Code hinter:

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(); 
    } 
} 

Dies ist bis 5 Einträge pro Tag (nicht 10 wie die gebaut begrenzt Unter Kontrolle, werden weitere ignoriert), sollten Ihnen jedoch eine Vorstellung davon geben, wie Sie das erreichen können, wonach Sie suchen, oder wenn Sie es ändern möchten.

+0

Ich schätze die Hilfe hier Matt. Das sieht nach einer machbaren Lösung aus und wird es als akzeptierte Antwort markieren. Allerdings habe ich gerade einen Weg gehackt, um mein Problem zu lösen. Grundsätzlich verberge ich den Kalender beim ersten Laden der Seite, und nachdem ich meine Daten zum ersten Mal geladen habe, erzwinge ich dann meinen Datumsbereich für den aktuellen Monat und zeige dann den Kalender an. Von nun an werden die Daten nur aktualisiert, wenn die Datumsbereiche aktualisiert werden, so dass das CalendarViewDayItemChanging ins Spiel kommt und ich auf dieser Basis aktualisieren kann. Ein bisschen hacky, aber es funktioniert. –