I have looked around at the other questions that relate to the DependencyProperty.UnsetValue in IMultiValueConverter, but I havent found an answer that relates to my problem, I think, so here goes:
The problem is Im getting "DependencyProperty.UnsetValue" whatever I try to do with the DataContext.
I have a WPF usercontrol, and in the constructor I create an object, like this:
public partial class Misc_Vehicles_GpsTrackBarContext : UserControl
{
private TimeLine TheTimeLine { get; set; }
public Misc_Vehicles_GpsTrackBarContext()
{
InitializeComponent();
DateTime start = DateTime.Now.AddDays(-1);
TheTimeLine = new TimeLine(start, DateTime.Now);
TheTimeLine.GpsLocations.Add(new GPSLocation(55.13, 13.7, start));
TheTimeLine.GpsLocations.Add(new GPSLocation(55.14, 13.6, start.AddMinutes(3)));
TheTimeLine.GpsLocations.Add(new GPSLocation(55.15, 13.5, start.AddHours(6)));
TheTimeLine.GpsLocations.Add(new GPSLocation(55.16, 13.4, start.AddHours(9)));
TheTimeLine.GpsLocations.Add(new GPSLocation(55.17, 13.3, start.AddHours(12)));
TheTimeLine.GpsLocations.Add(new GPSLocation(55.18, 13.2, start.AddHours(15)));
this.DataContext = this;
}
}
Note: I am now expecting the XAML to be able to access the TheTimeLine (like here http://www.wpf-tutorial.com/data-binding/using-the-datacontext/)
So, the "TheTimeLine" is an object that has some relevant data and the object I want to use when I in the XAML file want to "iterate through" the GPS positions:
class TimeLine
{
public DateTime TimeStart { get; set; }
public DateTime TimeEnd { get; set; }
public TimeSpan Duration
{
get
{
return TimeEnd.Subtract(TimeStart);
}
}
public ObservableCollection<GPSLocation> GpsLocations { get; set; } = new ObservableCollection<GPSLocation>();
public TimeLine(DateTime start, DateTime end)
{
if (start > end)
throw new ArgumentOutOfRangeException("The start parameter cannot be greater than the end parameter");
TimeStart = start;
TimeEnd = end;
}
}
class DriverSession
{
public DateTime TimeStart { get; set; }
public DateTime TimeEnd { get; set; }
public TimeSpan Duration
{
get
{
return TimeEnd.Subtract(TimeStart);
}
}
public DriverSession(DateTime start, DateTime end)
{
if (start > end)
throw new ArgumentOutOfRangeException("The start parameter cannot be greater than the end parameter");
TimeStart = start;
TimeEnd = end;
}
}
And, lastly, the XAML. AS can bee seen, the Binding tags under ItemsControl below use values from both TheTimeLine (TimeStart and TimeEnd) which is the same for each GPSPosition, and then uses the ReceivedTime that is in GPSLocation:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:tWorks.Alfa.OperatorClient.UserControls.Vehicles"
xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" x:Class="tWorks.Alfa.OperatorClient.UserControls.Vehicles.Misc_Vehicles_GpsTrackBarContext"
mc:Ignorable="d"
d:DesignHeight="260" Width="764.029">
<UserControl.Resources>
<LinearGradientBrush x:Key="HourBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF3EB1EA" Offset="0" />
<GradientStop Color="#FF61BFF1" Offset="0.5" />
<GradientStop Color="#FF01A1F4" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="MinuteBrush" EndPoint="0.999,0.51" StartPoint="0.045,0.51">
<GradientStop Color="#FFEDA25E" Offset="0" />
<GradientStop Color="#FFEDA25E" Offset="0.15" />
<GradientStop Color="#FFFA7A05" Offset="1" />
</LinearGradientBrush>
<local:MarginLengthConverter x:Key="mEventLengthConverter"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Rectangle Fill="AliceBlue"></Rectangle>
<Grid Grid.Row="1">
<Rectangle Margin="0" Height="2" Fill="{DynamicResource HourBrush}"/>
<!-- **** HERE IS THE ItemsControl! **** -->
<ItemsControl x:Name="GpsLocations" ItemsSource="{Binding Path=TheTimeLine.GpsLocations}">
<ItemsPanelTemplate>
<Grid x:Name="EventContainer" Height="20" Background="Gainsboro"/>
</ItemsPanelTemplate>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<!-- **** My rectangles (lines) to draw where I have GPS positions **** -->
<Rectangle StrokeThickness="0" Width="1" Fill="{DynamicResource MinuteBrush}">
<Rectangle.Margin>
<MultiBinding Converter="{StaticResource mEventLengthConverter}">
<Binding Path="TheTimeLine.TimeStart"/> <!-- when DataContext is set to "this", i expected TheTimeLine to be accessible? -->
<Binding Path="TheTimeLine.TimeEnd"/>
<Binding Path="ReceivedTime"/> <!-- ReceivedTime is inside an object called GPSLocation, that I am iterating through -->
<Binding ElementName="EventContainer" Path="ActualWidth"/>
</MultiBinding>
</Rectangle.Margin>
</Rectangle>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
And lastly, the annoying error =)
UPDATE
After I updated according to #mm8 comments, I now see this:
So, the values from the "TheTimeLine" fails...
The XAML part regarding the ItemsControl:
<!-- **** HERE IS THE ItemsControl! TheTimeLine.GpsLocations contains GPSLocation objects that has the ReceivedTime used below **** -->
<ItemsControl x:Name="GpsLocations" ItemsSource="{Binding Path=TheTimeLine.GpsLocations}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid x:Name="EventContainer" Height="20" Background="Gainsboro"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<!-- **** My rectangles (lines) to draw where I have GPS positions **** -->
<Rectangle StrokeThickness="0" Width="1" Fill="{DynamicResource MinuteBrush}">
<Rectangle.Margin>
<MultiBinding Converter="{StaticResource mEventLengthConverter}">
<Binding Path="TimeStart" RelativeSource="{RelativeSource AncestorType=UserControl}"/>
<Binding Path="TimeEnd" RelativeSource="{RelativeSource AncestorType=UserControl}"/>
<Binding Path="ReceivedTime"/> <!-- ReceivedTime is inside an object called GPSLocation, ObservableCollection<GPSLocation> -->
<Binding ElementName="EventContainer" Path="ActualWidth"/>
</MultiBinding>
</Rectangle.Margin>
</Rectangle>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can only bind to public properties:
public TimeLine TheTimeLine { get; set; }
Besides, the DataContext of an element in the ItemTemplate is a GPSLocation object assuming you have bound the ItemsSource property to an IEnumerable<GPSLocation>. If you want to bind to the TheTimeLine property of the parent UserControl class, you could use a RelativeSource:
<Binding Path="TheTimeLine.TimeStart" RelativeSource="{RelativeSource AncestorType=UserControl}"/>
Related
Let's say we have 25 products A and 14 products B. I want to create chart that is representing them using rectangles and grid. I wrote this code below and it works, but the chart generated with it is very inaccurate. Any ideas how to fix it?
<StackPanel Orientation="Horizontal">
<!--Products A-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.2*"/>
<RowDefinition Height="percentage1*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Red" HorizontalAlignment="Stretch" Grid.Row="1"/>
</Grid>
<!--Products B-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.2*"/>
<RowDefinition Height="percentage2*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Red" HorizontalAlignment="Stretch" Grid.Row="1"/>
</Grid>
</StackPanel>
percentage1 = 25 products / (25 products + 14 products)
percentage2 = 14 products / (25 products + 14 products)
You should not control the size of the Rectangle with the width of a Grid row. Instead host the Rectangle elements in a ItemsControl and calculate the final rendered width based on the available space:
rectangle_width = ratio * available_width
= value / max_value_in_chart * available_width
It's best to move the logic and layout to a custom Control or UserControl. And because the Rectangle bars are hosted in an ItemsControl, you can as many bars as you need to without any hassle (opposed to modifying a Grid to add more rows):
Usage
<local:SimpleBarChart x:Name="BarChart"
BarThickness="64"
Orientation="Horizontal"/>
public MainWindow()
{
InitializeComponent();
// Better use data binding
this.BarChart.BarValues = new List<double> { 12, 24, 36 };
}
SimpleBarChart.xaml.cs
public partial class SimpleBarChart : UserControl
{
public IList<double> BarValues
{
get => (IList<double>)GetValue(BarValuesProperty);
set => SetValue(BarValuesProperty, value);
}
public static readonly DependencyProperty BarValuesProperty = DependencyProperty.Register(
"BarValues",
typeof(IList<double>),
typeof(SimpleBarChart),
new PropertyMetadata(default));
public double BarThickness
{
get => (double)GetValue(BarThicknessProperty);
set => SetValue(BarThicknessProperty, value);
}
public static readonly DependencyProperty BarThicknessProperty = DependencyProperty.Register(
"BarThickness",
typeof(double),
typeof(SimpleBarChart),
new PropertyMetadata(default));
public SimpleBarChart()
{
InitializeComponent();
}
}
SimpleBarChart.xaml
<UserControl>
<UserControl.Resources>
<local:BarValueToLengthConverter x:Key="BarValueToLengthConverter" />
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=BarValues}"
HorizontalAlignment="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=HorizontalContentAlignment}"
HorizontalContentAlignment="Stretch"
VerticalAlignment="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=VerticalalContentAlignment}"
VerticalContentAlignment="Stretch">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Margin"
Value="0,0,0,12" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Fill="Orange"
Height="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=BarThickness}"
HorizontalAlignment="Left">
<Rectangle.Width>
<MultiBinding Converter="{StaticResource BarValueToLengthConverter}">
<Binding RelativeSource="{RelativeSource AncestorType=UserControl}"
Path="BarValues" />
<Binding />
<Binding RelativeSource="{RelativeSource AncestorType=UserControl}"
Path="BarThickness" />
<Binding RelativeSource="{RelativeSource AncestorType=ItemsControl}"
Path="ActualWidth" />
</MultiBinding>
</Rectangle.Width>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
BarValueToLengthConverter.cs
public class BarValueToLengthConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
IList<double>? barValues = values.OfType<IList<double>>().FirstOrDefault();
IList<double>? doubleValues = values.OfType<double>().ToList();
double barValue = doubleValues[0];
double barThickness = doubleValues[1];
double barHostWidth = doubleValues[2];
return barValue / barValues.Max() * barHostWidth;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
I have 2 classes, one inside the other, and a prop with an ObservableCollection of the class with the sub-class collection. But I'm having a serious trouble in displaying the whole thing.
First my data, this is what I've got: (it may clarify my issue)
public class MyItem
{
public string Id { get; set; }
public string Front { get; set; }
public Props.StateSemaphore Semaphore{ get; set; } // this is an enum w/ints
public string ToolTip { get; set; }
public string Architect { get; set; }
public string Status { get; set; }
public MyItem(){}
public MyItem(string id, string front,
Props.StateSemaphore semaphore, string toolTip,
string architect, string status)
{
Id = id;
Front = frente;
Semaphore = semaphore;
ToolTip = toolTip;
Architect = architect;
Status = status;
}
}
public class MyTab
{
public List<MyItem> MyItems { get; set; }
public string Environment { get; set; }
public MyTab() { }
public MyTab(string environment)
{
Environment = environment;
MyItems = new List<MyItem>();
}
}
And a prop on the PageExample.xaml.cs
private ObservableCollection<MyTab> myPanel;
public ObservableCollection<MyTab> MyPanel
{
get { return myPanel; }
set { myPanel = value; }
}
The idea is to display for each Environment a Grid of MyItems with an image(Red, Yellow or Green) on the semaphore enum
#Edit: This is almost working! Only the images won't display.
This is My XAML but im newbie on wpf so It's obvious I’m missing something.
<Page x:Class="MyBoard.PageMain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:w="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyBoard"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="PageMain">
<Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Center">
<DataGrid Name="EnvironmentDataGrid" IsReadOnly="True" ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" Header="Id"/>
<DataGridTextColumn Binding="{Binding Front}" Header="Front"/>
<DataGridTemplateColumn Header="Semaphore">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Semaphore}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding ToolTip}" Header="ToolTip"/>
<DataGridTextColumn Binding="{Binding Architect}" Header="Architect"/>
<DataGridTextColumn Binding="{Binding Status}" Header="Status"/>
</DataGrid.Columns>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Environment}" FontWeight="Bold" Padding="3"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=ItemCount}" Margin="8,0,4,0"/>
<TextBlock Text="Element(s)"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
</Grid>
</Page>
These are my questions:
How is the correct way to write the XAML? #Edit: DONE!
How and where to bind
the semaphore image to the datagrid?
#Edit: Semaphore is now a RelativeUri, because I didnt understand this answer.
I mean, I get the idea but not this thing:
<MultiBinding Converter={StaticResource catMultiConverter}>
<Binding .../>
<Binding .../>
</MultiBinding>
With the RelativeUri and all It still does not display.
See here to find out how to set DataGrid.Columns and how to Bind them.
Check here how to convert semaphore enums into Images thru Converter and DataGridTemplateColumn.CellTemplate.
See here how to use grouping in DataGrid to group on the Environment property so that same Environment items are shown arranged under one group.
I've got a ribbon bar in my silverlight app and on one of the icons I would like for there to be a badge icon showing the number of items in the view that the icon activates.
Picture the Mail icon in OS X showing the number of unread messages or the notifications counter on an IOS app icon.
I don't know much about xaml styles, but it seems to me I could duplicate the default style for the ribbon bar button, then add to it with some sort of red circle, and a white text that took in its value from a new property on the ribbon bar button somehow so I would be able to bind to it.
Does anyone have an example of something like this I can start from?
Thanks Shawn for the answer. This is what I ended up doing:
In the xaml:
<telerikRibbonBar:RadRibbonRadioButton
Text="Expired Active Call Factors"
Size="Large"
LargeImage="/CallFactorDatabase.UI;component/Images/Ribbon/Large/ExpiredActiveView.png"
Command="{Binding ActivateViewCommand}"
CommandParameter="ExpiredActiveView">
<Grid>
<Grid.Resources>
<converters:BooleanToVisibilityConverter x:Key="visibleWhenTrueConverter" VisibilityWhenTrue="Visible" VisibilityWhenFalse="Collapsed" />
</Grid.Resources>
<Grid Width="27" Height="27" Visibility="{Binding ExpiredActiveCallFactors, Converter={StaticResource visibleWhenTrueConverter}}" Margin="50,-40,0,0">
<Ellipse Fill="Black" Width="27" Height="27"/>
<Ellipse Width="25" Height="25" VerticalAlignment="Center" HorizontalAlignment="Center">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Coral" Offset="0.0" />
<GradientStop Color="Red" Offset="1.0" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Viewbox Width="25" Height="25" VerticalAlignment="Center" HorizontalAlignment="Center" >
<TextBlock Text="{Binding ExpiredActiveCallFactorsCount}" Foreground="White"/>
</Viewbox>
</Grid>
</Grid>
</telerikRibbonBar:RadRibbonRadioButton>
How it looks:
No luck getting it in front of the ribbon button but oh well.
This can be accomplished with a few bindings and an optional value converter. This samples assumes you are binding to a model that has an Items property and that that property is of type ObservableCollection so that the Count property of the collection will fire property changed when items are added/removed.
<Grid>
<Grid.Resources>
<local:CountToVisbilityConverter x:Key="CountToVis"/>
</Grid.Resources>
....
<Grid Width="25" Height="25" Visibility="{Binding Items.Count, Converter=CountToVis}">
<Ellipse Fill="Red" Width="25" Height="25"/>
<ViewBox Width="25" Height="25">
<TextBlock Text="{Binding Itmes.Count}" Foreground="White"/>
</Viewbox>
</Grid>
</Grid>
And the value converter:
public class CountToVisibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value == null) return Visibility.Collapsed;
int count = System.Convert.ToInt32(value);
return count == 0 ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
The reason I saw optional" converter is because you can also use the Interaction DataTriggers like such
<Grid x:Name="UnreadNotification" Width="25" Height="25">
<Ellipse Fill="Red" Width="25" Height="25"/>
<ViewBox Width="25" Height="25">
<TextBlock Text="{Binding Itmes.Count}" Foreground="White"/>
</Viewbox>
</Grid>
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding Items.Count, Comparison="Equal"
Value="0">
<ei:ChangePropertyAction PropertyName="IsEnabled"
Value="True"
TargetName="UnreadNotification" />
</ei:DataTrigger>
</i:Interaction.Triggers>
Here's my solution for this. By default, the badge will show in the upper right corner. You can change this by setting the "BadgeMarginOffset" property. I've attached a couple of images to show how it appears. One, which shows the badge wrapping a Telerik RadRibbonButton. You can also change the background and foreground colors of the badge (BadgeBackground, BadgeForeground). The defaults are shown below.
The UserControl's XAML
<UserControl x:Class="Foundation.Common.Controls.Wpf.Badge"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converters="clr-namespace:Foundation.Common.Controls.Wpf.Converters"
Background="Transparent" x:Name="UserControl"
mc:Ignorable="d" >
<UserControl.Resources>
<converters:GreaterThanZeroBooleanConverter x:Key="GreaterThanZeroBooleanConverter" />
<converters:GreaterThanZeroVisibilityConverter x:Key="GreaterThanZeroVisibilityConverter"/>
</UserControl.Resources>
<UserControl.Template>
<ControlTemplate>
<Grid HorizontalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border CornerRadius="10"
UseLayoutRounding="True"
x:Name="BadgeBorder"
Grid.ZIndex="99"
VerticalAlignment="Top"
HorizontalAlignment="Right"
Visibility="{Binding ElementName=UserControl, Path=Count, Mode=TwoWay, Converter={StaticResource GreaterThanZeroVisibilityConverter}}"
Grid.Row="0"
Margin="{Binding ElementName=UserControl, Path=BadgeMarginOffset}"
Height="22"
Padding="6.7,2,7,3"
Background="{Binding ElementName=UserControl, Path=BadgeBackground}">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=UserControl, Path=Count, Mode=TwoWay, Converter={StaticResource GreaterThanZeroBooleanConverter}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0.0"
To="1.0"
Duration="0:0:0.7"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=UserControl, Path=ShowDropShadow}" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="6" ShadowDepth="4" Color="#949494"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding ElementName=UserControl, Path=Count}"
Foreground="{Binding ElementName=UserControl, Path=BadgeForeground}"
FontWeight="Bold"
FontSize="12">
</TextBlock>
</Border>
<ContentPresenter Grid.Row="0"
Grid.RowSpan="2"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
</Grid>
</ControlTemplate>
</UserControl.Template>
The UserControl's code behind
public partial class Badge : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty CountProperty =
DependencyProperty.Register("Count", typeof(int), typeof(Badge));
public static readonly DependencyProperty ShowDropShadowProperty =
DependencyProperty.Register("ShowDropShadow", typeof(bool), typeof(Badge), new PropertyMetadata(true));
public static readonly DependencyProperty BadgeMarginOffsetProperty =
DependencyProperty.Register("BadgeMarginOffset", typeof(Thickness), typeof(Badge));
public static readonly DependencyProperty BadgeBackgroundProperty =
DependencyProperty.Register("BadgeBackground", typeof(Brush), typeof(Badge), new PropertyMetadata(Brushes.Red));
public static readonly DependencyProperty BadgeForegroundProperty =
DependencyProperty.Register("BadgeForeground", typeof(Brush), typeof(Badge), new PropertyMetadata(Brushes.White));
#endregion Dependency Properties
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="Badge"/> class.
/// </summary>
public Badge()
{
this.InitializeComponent();
}
#endregion Constructor
#region Properties
/// <summary>
/// Gets or sets a value indicating whether [show drop shadow].
/// </summary>
/// <value>
/// <c>true</c> if [show drop shadow]; otherwise, <c>false</c>.
/// </value>
public bool ShowDropShadow
{
get => (bool)this.GetValue(ShowDropShadowProperty);
set => this.SetValue(ShowDropShadowProperty, value);
}
/// <summary>
/// Gets or sets the badge margin offset.
/// </summary>
/// <value>
/// The badge margin offset.
/// </value>
public Thickness BadgeMarginOffset
{
get => (Thickness)this.GetValue(BadgeMarginOffsetProperty);
set => this.SetValue(BadgeMarginOffsetProperty, value);
}
/// <summary>
/// Gets or sets the badge background.
/// </summary>
/// <value>
/// The badge background.
/// </value>
public Brush BadgeBackground
{
get => (Brush)this.GetValue(BadgeBackgroundProperty);
set => this.SetValue(BadgeBackgroundProperty, value);
}
/// <summary>
/// Gets or sets the badge foreground.
/// </summary>
/// <value>
/// The badge foreground.
/// </value>
public Brush BadgeForeground
{
get => (Brush)this.GetValue(BadgeForegroundProperty);
set => this.SetValue(BadgeBackgroundProperty, value);
}
/// <summary>
/// Gets or sets the count.
/// </summary>
/// <value>
/// The count.
/// </value>
public int Count
{
get => (int)this.GetValue(CountProperty);
set => this.SetValue(CountProperty, value);
}
#endregion Properties
}
Sample code for the top two images
<wpf:Badge Count="108" Margin="20" HorizontalAlignment="Left" BadgeMarginOffset="0,-5,-5,0">
<Button Height="100" Width="100">
<Button.Content>
<Image Source="Resources/about.png" />
</Button.Content>
</Button>
</wpf:Badge>
See the following code,
After I clicked the button, the listbox render several times.
How can I prevent Listbox flickering?
Is it possible to tell a control stop update/render?
<UserControl x:Class="SilverlightApplication52.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid x:Name="LayoutRoot"
Background="Gray"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ListBox x:Name="listbox"
Background="White"
Margin="100">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}"
Height="{Binding Height}"
Fill="{Binding Background}"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="{Binding Scale}"
ScaleY="{Binding Scale}" />
<RotateTransform Angle="{Binding Angle}" />
<TranslateTransform X="{Binding Left}"
Y="{Binding Top}" />
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="test"
Width="50"
Height="50"
Click="Button_Click" />
</Grid>
public partial class MainPage : UserControl
{
public class ItemInfo
{
public double Left { get; set; }
public double Top { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double Angle { get; set; }
public double Scale { get; set; }
public Brush Background { get; set; }
}
ObservableCollection<ItemInfo> _items = new ObservableCollection<ItemInfo>();
public MainPage()
{
InitializeComponent();
listbox.ItemsSource = _items;
}
Random random = new Random();
private void Button_Click(object sender, RoutedEventArgs e)
{
_items.Clear();
for (int i = 0; i < 2000; i++)
{
byte r = (byte)(random.NextDouble()*255);
byte g = (byte)(random.NextDouble()*255);
byte b = (byte)(random.NextDouble()*255);
_items.Add(
new ItemInfo
{
Left = random.NextDouble() * 500,
Top = random.NextDouble() * 500,
Width = random.NextDouble() * 1000,
Height = random.NextDouble() * 1000,
Angle = random.NextDouble() * 359,
Scale = random.NextDouble() * 1,
Background = new SolidColorBrush(Color.FromArgb(255,r,g,b)),
}
);
}
}
}
Try adding to a separate ObservableCollection in that loop (that is not referenced/bound to the listbox). Then when the loop is done assign the listbox ItemsSource to the new observablecollection.
I am loading values for a listbox from an xml file. What my problem is i can't get the bindings show the property values of the class that each item is assigned. When i set the Text this way:
<TextBlock Text="{Binding }" Style="{StaticResource TitleBlock}"></TextBlock>
The items show the toString value of the class, but if i use:
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleBlock}"></TextBlock>
I get blank space for each Item in the list box. I hope i have explained my problem well enough. Code posted below:
MapList.xml
<Maps>
<map>
<title>Backlot</title>
<id>mp_backlot</id>
<description>Daytime urban combat.</description>
<thumbnail>mapImages/map11.jpg</thumbnail>
</map>
<map>
<title>Bloc</title>
<id>mp_bloc</id>
<description>Snowy close quarters combat with some sniping available.</description>
<thumbnail>mapImages/map11.jpg</thumbnail>
</map>
<map>
<title>The Bog</title>
<id>mp_bog</id>
<description>Night time map great for any play style.</description>
<thumbnail>mapImages/map11.jpg</thumbnail>
</map>
</Maps>
MainPage.xaml :
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" x:Class="Cod4ServerTool.MainPage" Height="521" Width="928">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="0"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.Background>
<ImageBrush Stretch="Uniform" ImageSource="ui_bg.jpg"/>
</Grid.Background>
<controls:TabControl Margin="0,8,0,0" Grid.Row="1">
<controls:TabControl.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#662A2C12" Offset="1"/>
</LinearGradientBrush>
</controls:TabControl.Background>
<controls:TabItem Header="TabItem" Foreground="Black">
<Grid>
<ListBox x:Name="MapsList_lb" Margin="8,8,177,8">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ThumbNail}" Style="{StaticResource ThumbNailPreview}"></Image>
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleBlock}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#66F0F2F0" Offset="0.254"/>
<GradientStop Color="#CC828C82" Offset="1"/>
<GradientStop Color="#CCD5DED6"/>
</LinearGradientBrush>
</ListBox.Background>
</ListBox>
<ListBox Margin="0,8,8,8" HorizontalAlignment="Right" Width="160">
<ListBox.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#66F0F2F0" Offset="0.254"/>
<GradientStop Color="#CC828C82" Offset="1"/>
<GradientStop Color="#CCD5DED6"/>
</LinearGradientBrush>
</ListBox.Background>
</ListBox>
</Grid>
</controls:TabItem>
<controls:TabItem Header="TabItem">
<Grid/>
</controls:TabItem>
</controls:TabControl>
<Button Height="21" HorizontalAlignment="Right" Margin="0,8,8,0" VerticalAlignment="Top" Width="95" Content="Import Maps" Grid.Row="1" Click="Button_Click"/>
</Grid>
</UserControl>
the .cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml.Linq;
namespace Cod4ServerTool
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DisplayMaps("MapList.xml");
}
private void DisplayMaps(string xmlContent)
{
XDocument xmlMaps = XDocument.Load(xmlContent);
var maps = from map in xmlMaps.Elements("Maps").Elements("map")
select new Map
{
Id = map.Element("id").Value,
Title = map.Element("title").Value,
Description = map.Element("description").Value,
ThumbNail = map.Element("thumbnail").Value,
};
MapsList_lb.SelectedIndex = -1;
MapsList_lb.ItemsSource = maps;
}
}
}
Map.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Cod4ServerTool
{
class Map
{
public string Title { get; set; }
public string Id { get; set; }
public string Description { get; set; }
public string ThumbNail { get; set; }
public override string ToString()
{
return Title;
}
}
}
I would make the maps variable into a List by just adding .ToList();
private void DisplayMaps(string xmlContent)
{
XDocument xmlMaps = XDocument.Load(xmlContent);
var maps = (from map in xmlMaps.Elements("Maps").Elements("map")
select new Map
{
Id = map.Element("id").Value,
Title = map.Element("title").Value,
Description = map.Element("description").Value,
ThumbNail = map.Element("thumbnail").Value,
}).ToList();
MapsList_lb.SelectedIndex = -1;
MapsList_lb.ItemsSource = maps;
}
Edit:
D'oh! and your Map class needs to be declared public. Binding doesn't work with internal types.
The above advice to use ToList still stands, its better for ItemsSource to reference a simple List<Map> than it is to reference a LINQ query that in turns holds on to a tree of XObjects.
Implement interface INotifyPropertyChanged of Map class:
public class Map : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _Title;
public string Title
{
get { return _Title; }
set
{
_Title = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Title"));
}
}
}
...
}
It will work fine after that.
Update:
Make Map class public.