I am learning WPF and a newbie. I have this requirement. In the view model I have a timespan object. I would like to display the timespan as "one filled circle for each hour". What will be an optimal way to do this in WPF way. I tried the ItemsTemplate but couldnot find a way to do this.
3 回答
Have a look at this article about creating a Rating Control It would be very similar.
The only MVVM thing about this might be how you gather the actual value that should be shown in the control. But building a control hasn't much to do with MVVM.
Let us know if you get stuck and try to ask specific questions.
One option is to create custom control that will generate to correct number of circles in code but if you prefer to mainly use XAML you have a few options.
If you want the circles to be generated inside a panel you can use an ItemsControl
to perform the generation. The items generated are the circles so you need a view-model property with an element for each whole hour. These elements does not have any properties and can simply be of type Object
:
public ObservableCollection<Object> Items { get; private set; }
You can update the number of objects in the collection:
Items.Clear();
var item = new Object();
for (var i = 0; i < timeSpan.Hours; i += 1)
Items.Add(item);
A simple view is then:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="25" Height="25" Fill="Black" StrokeThickness="0" Margin="10"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to layout the circles in a manner that is hard to do using a panel you can instead manually layout 24 circles in the XAML. You can then use a value converter to hide or show each circle based on a view-model property representing the hours:
// Remember to fire INotifyPropertyChanged.PropertyChanged when value changes.
public Int32 Hours { get; private set; }
The value converter will then convert the number into a visibility:
class VisibilityConverter : IValueConverter {
public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture) {
var hours = System.Convert.ToInt32(value);
var visibleBelow = System.Convert.ToInt32(parameter);
// Alternatively use Visibility.Collapsed to completely remove the circle.
return hours <= visibleBelow ? Visibility.Visible : Visibility.Hidden;
}
public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
Each Ellipse
will then bind the Visibility
to the Hours
property using a ConverterParameter
that identifies the hour of the Ellipse
:
<Ellipse
Visibility="{Binding Hours, Converter={StaticResource VisibilityConverter}, ConverterParameter=3}"/>
There are many ways to achieve this. Here is one you can do in raw XAML
- Databind an
ItemsControl
to a collection of booleans generated by your view models - one bool for each hour represented. - Set
ItemsControl.ItemTemplate
to a template that includes anEllipse
with the same height and width and a border of [colour] - Use Actions / behaviors to set the Fill of the
Ellipse
to [colour] when theDataContext
equals System.Boolean true
With this method, you will need to call raise PropertyChanged on the entire boolean collection each time one of them changes.