Я ищу способ связать дату и время, но отобразить разницу с текущим временем.
Вот код С#:
public partial class MainWindow : Window
{
public ObservableCollection<Information> Informations;
public MainWindow()
{
InitializeComponent();
Informations = new ObservableCollection<Information>
{
new Information() { When = DateTime.Now, What = "A first information" },
new Information() { When = DateTime.Now, What = "Another information" },
new Information() { When = DateTime.Now, What = "An other information" }
};
ltvInfos.ItemsSource = Informations;
}
}
public class Information : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string What
{
get { return what; }
set { what = value; OnPropertyChanged(); }
}
private string what;
public DateTime When
{
get { return when; }
set { when = value; OnPropertyChanged(); }
}
private DateTime when;
}
И WPF:
<ListView x:Name = "ltvInfos">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation = "Horizontal">
<TextBlock Text = "{Binding When}" Margin = "5,0"/>
<TextBlock Text = "{Binding What}" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>`
Вот результат:
И я ищу, чтобы отобразить что-то вроде этого:
Я не нашел никакого способа сделать это, кроме запуска таймера, который обновляет другое свойство каждую секунду, чтобы вычислить разницу с текущим временем.
@emoacht, потому что мне кажется сложным обновлять каждую секунду мою коллекцию информации, которая будет содержать тысячи объектов. Если есть способ просто «перевести» это с помощью своего рода привязки, это будет гораздо оптимальнее!
Как вы правильно указали в своем вопросе, вы запустите таймер, который обновляет свойства каждые X секунд.
Обновление структуры DateTime обходится довольно дешево, а при использовании WPF с привязками данных по умолчанию отображаются только отображаемые элементы, что значительно повышает производительность.
1000 объектов – не проблема. Это станет заметно только после того, как вы достигнете отметки в 1 000 000, и в этот момент вам придется пересмотреть свой дизайн.
Значит, не существует «простого способа» естественным образом отобразить разницу во времени без использования определенного свойства?
Нет, вам нужно закодировать это самостоятельно.
Хорошо. Есть ли способ получить информацию о том, что отображается список? Например, с логической привязкой IsDisplayed, и когда это логическое значение истинно, время также включено.
Да, вы можете привязать свойство Visibility к своему TextBlock и добавить в свой класс Information
свойство, которое возвращает System.Windows.Visibility
.
Видимость будет ложной, если я прокрутлю, а линия объекта не отобразится?
Если вы хотите сэкономить ресурсы (нужно ли вам это делать, это другая история), вы можете: 1) поместить логику таймера в поле зрения, чтобы избежать обновления при каждом такте модели представления, 2) разделить таймер между экземплярами, 3) Используйте виртуализацию.
Допустим, создайте пользовательский TextBlock
, статический член которого имеет таймер, и поделитесь им между экземплярами.
public class TimerTextBlock : TextBlock
{
private static DispatcherTimer _timer;
static TimerTextBlock()
{
_timer = new() { Interval = TimeSpan.FromSeconds(1) };
_timer.Start();
}
public DateTime Time
{
get { return (DateTime)GetValue(TimeProperty); }
set { SetValue(TimeProperty, value); }
}
public static readonly DependencyProperty TimeProperty =
DependencyProperty.Register(
"Time",
typeof(DateTime),
typeof(TimerTextBlock),
new PropertyMetadata(default(DateTime), OnTimeChanged));
private static void OnTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TimerTextBlock)d).IsRunning = ((DateTime)e.NewValue != default);
}
private bool _isRunning;
public bool IsRunning
{
get => _isRunning;
set
{
if (_isRunning != value)
{
_isRunning = value;
if (_isRunning)
{
_timer.Tick += OnTick;
}
else
{
_timer.Tick -= OnTick;
}
}
}
}
private void OnTick(object? sender, EventArgs e)
{
this.Text = (DateTime.Now - Time).ToString(/* Specify text format */);
}
}
И если предположить, что ваша модель представления имеет коллекцию DateTime
с именем «Times» как общедоступное свойство только для чтения, простой ListBox, который принимает эту коллекцию, будет следующим:
<ListBox ItemsSource = "{Binding Times}"
VirtualizingStackPanel.IsVirtualizing = "True"
VirtualizingStackPanel.VirtualizationMode = "Recycling">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<local:TimerTextBlock Time = "{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Это очень хороший способ, тай!
Это один из простых способов. Почему бы вам это не реализовать?