Positioning of Axis Label in a DateTimeAxis - c#

At the moment I have a date time axis where the date is in-line with the points, is there anyway to get this date to appear in the center such as on a bar chart.
<Style x:Key="DateTimeAxisLabelStyle2" TargetType="chartingToolkit:DateTimeAxisLabel">
<Setter Property="DaysIntervalStringFormat" Value="{}{0:dd-MMM}" />
<Setter Property="HoursIntervalStringFormat" Value="{}{0:hh:mm tt}" />
<!--<Setter Property="RenderTransformOrigin" Value="1,0.5" />
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="-45" />
</Setter.Value>
</Setter>-->
<!--<Setter Property="Margin" Value="30,0,-10,0" />-->
</Style>
<chartingToolkit:DateTimeAxis IntervalType="Days"
Interval="1"
Minimum="{Binding StartDate}"
Maximum="{Binding EndDate}"
Orientation="X"
VerticalContentAlignment="Center"
Title="Day"
AxisLabelStyle="{StaticResource DateTimeAxisLabelStyle2}" />
Any help would be greatly appreciated.

Here's what i got:
XAML:
<Window.Resources>
<Style x:Key="DateTimeAxisLabelStyle1" TargetType="{x:Type chartingToolkit:DateTimeAxisLabel}">
<Setter Property="DaysIntervalStringFormat" Value="{}{0:dd-MMM}"></Setter>
<Setter Property="RenderTransformOrigin" Value="0.80,0.20"></Setter>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="-90"></RotateTransform>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<chartingToolkit:Chart Margin="0" Title="Chart Title">
<chartingToolkit:Chart.DataContext>
<local:MyDataCollection/>
</chartingToolkit:Chart.DataContext>
<chartingToolkit:Chart.Axes>
<chartingToolkit:DateTimeAxis Minimum="{Binding StartDate}" Maximum="{Binding EndDate}" Orientation="X" ShowGridLines="True" AxisLabelStyle="{DynamicResource DateTimeAxisLabelStyle1}"/>
</chartingToolkit:Chart.Axes>
<chartingToolkit:LineSeries DependentValuePath="Y" IndependentValuePath="X" ItemsSource="{Binding}"/>
</chartingToolkit:Chart>
</Grid>
Chart:

Here's what I did using the WPF Toolkit Source for reference.
I created a custom class deriving from DateTimeAxis, then overrode the "GetPlotAreaCoordinate" method. The DateTimeAxis.Render() calls that method three times with the same list of "DateTime" values, once for the MajorTickmarks, once for MinorTickmarks, and once for the date label. There were no minor tickmarks in the list, so the method was actually only getting called twice. I just keep a list of the values that have been evaluated and assume that if it's in the list it's already done the tickmarks and is now doing the Labels.
class CustomDateTimeAxis : DateTimeAxis
{
List<object> _valueList = new List<object>();
UnitValue prevBaseValue;
protected override UnitValue GetPlotAreaCoordinate(object value, Range<IComparable> currentRange, double length)
{
_valueList.Add(value);
UnitValue baseValue = base.GetPlotAreaCoordinate(value, currentRange, length);
int valueCount = _valueList.Count((x) => x.Equals(value));
if (valueCount == 2)
return new UnitValue(baseValue.Value + 27, baseValue.Unit);
prevBaseValue = baseValue;
return baseValue;
}
protected override void Render(Size availableSize)
{
base.Render(availableSize);
_valueList.Clear();
}
}
"27" is just a number I was trying out. You might want to play with that to see what works best for you.
return new UnitValue(baseValue.Value + 27, baseValue.Unit);

I created a Margin-Converter:
public class MarginConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var dateTimeAxis = values[0] as DateTimeAxis; ;
var actualAxisLength = values[1] as double?;
var actualMaximum = values[2] as DateTime?;
var actualMinimum = values[3] as DateTime?;
if (dateTimeAxis == null ||
!dateTimeAxis.Interval.HasValue ||
!actualAxisLength.HasValue ||
!actualMaximum.HasValue ||
!actualMinimum.HasValue)
return null;
double xMargin = 0;
var interval = dateTimeAxis.Interval.Value;
var timeSpan = actualMaximum.Value - actualMinimum.Value;
var timeSpanInDays = timeSpan.TotalDays;
if (dateTimeAxis.IntervalType == DateTimeIntervalType.Months)
{
xMargin = 30 * interval * actualAxisLength.Value / timeSpanInDays;
}
else if (dateTimeAxis.IntervalType == DateTimeIntervalType.Days)
{
xMargin = interval * actualAxisLength.Value / timeSpanInDays;
}
return new Thickness(xMargin, 10, 0, -30);
}
public object[] ConvertBack(object value, System.Type[] targetType, object parameter, CultureInfo culture)
{
return null;
}
}
called the X-Axis 'SharedXAxis' and used the converter like this:
<Setter Property="Margin">
<Setter.Value>
<MultiBinding Converter="{StaticResource MarginConv}">
<Binding ElementName="SharedXAxis"/>
<Binding ElementName="SharedXAxis" Path="ActualWidth"/>
<Binding ElementName="SharedXAxis" Path="ActualMaximum"/>
<Binding ElementName="SharedXAxis" Path="ActualMinimum"/>
</MultiBinding>
</Setter.Value>
Imo this should be full dynamic.
The Top- and Bottom-Values of the Thickness of the Margin-Converter ('10' and '-30' in my case) as well as the Bottom-Value of the Padding of the Chart itself have to be adjusted, I don't know why.

Related

WPF Listview alternate row background by data group not row

I couldn't find a solution for my problem/idea, and I hope that someone could help me out.
In WPF I have a CollectionViewSource depending on an IEnumerable<Item>.
An Item has the Name, Height, Age fields.
In Xaml, the ListView's ItemsSource="{Binding CollectionViewSource.View}".
I know, the Listview has an AlternationCount property which can change the row background color.
But I want to change the row background color only if the Age field data is different from the above row's Age data.
Like this, ordered by Age:
The row background color is alternated only when the Age data is different.
When I set another sort order to the list, the alternating should also be changed.
In this picture the list is ordered by Name:
But the background color depends on the Age data.
Is there any way to make a solution for this concept?
You can use the RelativeSource.PreviousData for your task.
First, create an IMultivalueConverter which will accept the values you want to compare and return the current alternation index based on them:
class ComparisonConverter : IMultiValueConverter
{
private int currentAlternation = 0;
public int AlternationCount { get; set; } = 2;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// TODO: exception handling
if (values.Distinct().Count() != 1)
{
if (++currentAlternation >= AlternationCount)
{
currentAlternation = 0;
}
}
return currentAlternation;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This converter accepts multiple values and returns an unchanged alternation index when all values are equal; otherwise, it first changes the alternation index to the next one and then returns a new index.
Now, create a MultiBinding that will provide the alternation index value to the Style, where you define your colors:
<!-- This is an incomplete ListView! Set the View and ItemsSource as required. -->
<ListView>
<ListView.Resources>
<local:ComparisonConverter x:Key="ComparisonConverter"/>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<!-- This is the DataTrigger for the alternation index 1 -->
<DataTrigger Value="1">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ComparisonConverter}">
<Binding Path="Age"/>
<Binding Path="Age" RelativeSource="{RelativeSource PreviousData}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="Background" Value="Wheat"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
So in this style, the default Color for the alternation index 0 is Wheat. Using the DataTrigger, the alternation index 1 will generate the color Red.
The sort order changes will be reflected automatically, because the CollectionViewSource will rebuild the view, so the ListView will create all items from scratch using the MultiBinding for each item.
Run this and live life to fullest :
XAML :
<Window x:Class="WpfStackOverflow.Window6"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window6" Height="362.03" Width="563.91">
<Window.Resources>
<CollectionViewSource x:Key="CVS" Source="{Binding .}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Age"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<ListView x:Name="LstView" ItemsSource="{Binding Source={StaticResource CVS}}">
<ListView.Resources>
<AlternationConverter x:Key="AltCnvKey">
<SolidColorBrush Color="Snow"/>
<SolidColorBrush Color="LightBlue"/>
<SolidColorBrush Color="Orange"/>
</AlternationConverter>
</ListView.Resources>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Name"/>
<GridViewColumn DisplayMemberBinding="{Binding Height}" Header="Height"/>
<GridViewColumn DisplayMemberBinding="{Binding Age}" Header="Age"/>
</GridView.Columns>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle AlternationCount="3">
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="StackPanel">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource AncestorType=GroupItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex), Converter={StaticResource AltCnvKey}}"/>
</Style>
</StackPanel.Resources>
<ItemsPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
Code :
using System.Linq;
using System.Windows;
namespace WpfStackOverflow
{
/// <summary>
/// Interaction logic for Window6.xaml
/// </summary>
public partial class Window6 : Window
{
public Window6()
{
InitializeComponent();
this.DataContext = new[] { new { Age = 32, Name = "Name1", Height = 6 }, new { Age = 34, Name = "Name1", Height = 6 }, new { Age = 34, Name = "Name1", Height = 6 }, new { Age = 32, Name = "Name1", Height = 6 }, new { Age = 32, Name = "Name1", Height = 6 }, new { Age = 39, Name = "Name1", Height = 6 }, new { Age = 40, Name = "Name1", Height = 6 } }.ToList();
}
}
}

Multivalue Bound Textblock DataTrigger not firing

So I'm trying to Bind a TextBlock to multiple values on my Viewmodel (Mix of Enums and Strings). I have a DataTrigger that is supposed to fire when the text is null when returned by the Converter. But it doesn't! At first, I thought my Style didn't take hold (hence changed the Background on the Style to show it did). Anyway here is the code
XAML
<TextBlock x:Name="MyTextBlock" Grid.Column="2" Grid.ColumnSpan="3" VerticalAlignment="Center" DataContext="{StaticResource ViewModelLocator}"
Margin="{Binding RelativeSource={RelativeSource Self}, Path=(params:General.BoldPadding), Mode=OneWay}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource GeneralMultiStringDisplayConverter}">
<Binding Path="RatesViewModel.Instrument.Currency" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="RatesViewModel.Instrument.Underlying" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="RatesViewModel.Instrument.ProductType" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</TextBlock.Text>
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource HeaderTextStyle}">
<Setter Property="Background" Value="Blue"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyTextBlock, Path=Text}" Value="{x:Null}"> <!--THIS SHOULD FIRE-->
<Setter Property="Text" Value="ThisShouldFireOnStart"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Resources>
</TextBlock>
The Converter is as follows:
class GeneralMultiStringDisplayConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var AS = DependencyProperty.UnsetValue;
if (values[0] != AS )
{
int count = values.Count();
string result = string.Empty;
for (int i = 0; i < count - 1; ++i)
{
try
{
var A = Enum.GetName((values[i].GetType()), values[i]);
result = String.Format("{0}{1}.", result, A);
}
catch (Exception ex)
{
result = String.Format("{0}{1}.", result, values[i]);
}
}
result = String.Format("{0}{1}", result, values[count - 1]);
return result;
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
//TODO:
}
}
Debugging Steps that I have taken
`<Setter Property="Text" Value="ABC"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyTextBlock, Path=Text}" Value="ABC">
<Setter Property="Text" Value="ThisShouldFireOnStart"/>
</DataTrigger>
</Style.Triggers>
`
Added a converter to the Styles DataTrigger Binding. It always gets "" as a parameter and not null for some reason. Setting the trigger Value to "" does not work.
Added a default Text property in the Style and tried changing the Value based off of that. (See example above)
I would appreciate some help in getting this to work
Thanks!
You can not override these local bindings with a DataTrigger in a Style.
See Why doesn’t my WPF Trigger work?
If you just need to show a string if your bindings are null use TargetNullValue="MyNullValueString"
Perhaps you need a if(value[0] != null) condition in your converter to identify unset properties. Since i do not have any detail of your RatesViewModel i can not say for sure.
<MultiBinding Converter="{StaticResource GeneralMultiStringDisplayConverter}" TargetNullValue="ThisIsNull">
<Binding Path="RatesViewModel.Instrument.Currency" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="RatesViewModel.Instrument.Underlying" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="RatesViewModel.Instrument.ProductType" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>

Exception in MultiBinding when Application.DispatcherUnhandledException is registered

<UserControl.Resources>
<DataTemplate x:Key="EditCardNonBuffer">
...
...
<ComboBox.SelectedValue>
<MultiBinding Mode="TwoWay" Converter="{StaticResource ingredientRowToTypeIDConverter}">
<Binding Path="GridIngredient.IngredientIngredientTypeRow" />
<Binding Path="GridIngredient.IngredientStockRowByIngredientStockGridIngredient" />
</MultiBinding>
</ComboBox.SelectedValue>
...
...
</DataTemplate>
<DataTemplate x:Key="EditCardBuffer">
...
...
<ComboBox.SelectedValue>
<MultiBinding Mode="TwoWay" Converter="{StaticResource ingredientRowToTypeIDConverter}">
<Binding Path="GridIngredient.IngredientIngredientTypeRow" />
<Binding Path="GridIngredient.IngredientStockRowByIngredientStockGridIngredient" />
</MultiBinding>
</ComboBox.SelectedValue>
...
...
</DataTemplate>
</UserControl.Resources>
<Border BorderBrush="Black" BorderThickness="1" Background="White" Margin="2,-4,2,0" Visibility="{Binding GridIngredient.IsEditMode, Converter={StaticResource GlobalBooleanToVisibilityConverter}}">
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding GridIngredient.IngredientIngredientTypeRow.IngredientTypeRow.IsBuffer}" Value="False">
<Setter Property="ContentTemplate" Value="{StaticResource EditCardNonBuffer}" />
</DataTrigger>
<DataTrigger Binding="{Binding GridIngredient.IngredientIngredientTypeRow.IngredientTypeRow.IsBuffer}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditCardBuffer}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Border>
Converter:
public class IngredientRowToTypeIDConverter : IMultiValueConverter
{
private SystemDataSet _dataSet;
public object Convert( object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture )
{
if( values.Length > 0 && values[ 0 ] is SystemDataSet.IngredientIngredientTypeRow )
{
var row = (SystemDataSet.IngredientIngredientTypeRow) values[ 0 ];
_dataSet = _dataSet ?? (SystemDataSet) row.Table.DataSet;
return row.IngredientTypeID;
}
else
MessageBox.Show( "just for debugging" );
return values;
}
public object[] ConvertBack( object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture )
{
if( value is int && _dataSet != null )
{
var ingredientTypeID = (int) value;
var ingredient = _dataSet.IngredientIngredientType.Where( r => r.IngredientTypeID == ingredientTypeID ).OrderBy( r => r.IngredientRow.LongName ).First();
var ingredientStock = ingredient.IngredientTypeRow.IsBuffer ? ingredient.IngredientRow.IngredientStockRowByDefaultBuffer : ingredient.IngredientRow.IngredientStockRowByDefaultNonBuffer;
return new object[] { ingredient, ingredientStock };
}
else
MessageBox.Show( "just for debugging" );
return new object[] { value };
}
}
Following exception is caught by Application.DispatcherUnhandledException event when I change selected value of combo box.
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
at System.Collections.Generic.List`1.get_Item(Int32 index)
at System.Windows.Data.MultiBindingExpression.UpdateSource(Object convertedValue)
at System.Windows.Data.BindingExpressionBase.UpdateValue()
at System.Windows.Data.BindingExpressionBase.Dirty()
at System.Windows.Data.BindingExpressionBase.SetValue(DependencyObject d, DependencyProperty dp, Object value)
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetCurrentValueInternal(DependencyProperty dp, Object value)
at System.Windows.Controls.Primitives.Selector.UpdatePublicSelectionProperties()
at System.Windows.Controls.Primitives.Selector.SelectionChanger.End()
at System.Windows.Controls.Primitives.Selector.SelectionChanger.SelectJustThisItem(ItemInfo info, Boolean assumeInItemsCollection)
at System.Windows.Controls.ComboBoxItem.OnMouseLeftButtonUp(MouseButtonEventArgs e)
But I don't get it when the global exception handler event is not registered.
EDIT:
Notice that one of the bound values of combo box is also bound to a data trigger of the content control to change its data template. The exception ONLY happens when the change in combo box is supposed to change the template as well. The ContentTemplate changes successfully. But why the exception?

Separate text in a Textbox with a drawn line?

So here it goes, I making a c# project in wpf and im stuck at trying to make a text box with a line that separates text.
At the moment i got the textbox like this:
Instead of using "-------" i want to make a predefined line in the textbox. Is this possible ?
Agg. The textbox is editable in runtime
The drawn line should have the properties:
It should not be edit able !
It shouldn't only be an empty line
It should be a visible line that has the width line.width = box.width !
If you don't need a full textbox implementation then this might help. It's got real issues with not showing the cursor ect but might give you a start.
first add the following converter to your project.
public class TextLineConverter : MarkupExtension, IValueConverter
{
static TextLineConverter converter;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string[] results = null;
string newText = value as string;
if (newText != null)
{
results = newText.Split('\r');
if (results.Length > 0)
for (int i = 0; i < results.Length; i++)
if (results[i].Length > 0)
if (results[i][0] == '\n')
results[i] = results[i].Substring(1, results[i].Length - 1);
}
return results;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (converter == null) converter = new TextLineConverter();
return converter;
}
public TextLineConverter()
{
}
}
And the following style.
<Style TargetType="TextBox">
<Style.Resources>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="0,0,0,2" BorderBrush="Black" >
<ContentPresenter Content="{Binding}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<ListView Focusable="False" ItemsSource="{Binding Text, Converter={local:TextLineConverter}, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
On rereading your question, i believe that the best way to do what you want is to create your own user-control that has an attribute specifically for this. Do some research on C# User Controls and perhaps you'll find out how to do this.

How do I create a ProgressBar which is hidden when the Value is 0?

The following doesn't quite work (ProgressValue is a value set in DataContext.)
<StatusBarItem Grid.Column="1">
<StatusBarItem.Resources>
<Style TargetType="ProgressBar">
<Style.Triggers>
<DataTrigger Binding="{Binding ProgressValue}" Value="0">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBarItem.Resources>
<ProgressBar Value="{Binding ProgressValue}" Grid.Column="1" Width="80" Height="13">
</ProgressBar>
</StatusBarItem>
Try setting the ProgressBar's Visibility property in its ValueChanged event:
private void progressBar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
progressBar1.Visibility = (progressBar1.Value == 0) ? Visibility.Hidden : Visibility.Visible;
}
Try specifying a Minimum (and Maximum) value. After setting those it seems to work for my version of your code.
With this XAML:
<StatusBarItem x:Name="Feedback" Grid.Row="1">
<StatusBarItem.Resources>
<Style TargetType="ProgressBar">
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="0">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBarItem.Resources>
<ProgressBar Value="{Binding Mode=OneWay}" Minimum="0" Maximum="10" Grid.Column="1" Width="80" Height="13">
</ProgressBar>
</StatusBarItem>
and this code in the constructor of MainWindows.xaml.cs (for testing purposes only)
int value = 10;
public MainWindow()
{
InitializeComponent();
Feedback.DataContext = value;
Timer t = new Timer(500);
t.Elapsed += (s, e) =>
{
if (value > 0) Dispatcher.Invoke(new Action(() => { Feedback.DataContext = --value; }));
else t.Stop();
};
t.Start();
}
I get the progress bar to go from 10 down to 0 at which point the progress bar disappears.
Example XAML:
<ProgressBar>
<ProgressBar.Resources>
<src:DoubleToVisibilityConverter x:Key="_doubleToVisibilityConverter" />
</ProgressBar.Resources>
<ProgressBar.Visibility>
<Binding
RelativeSource="{RelativeSource Self}"
Path="Value"
Converter="{StaticResource _doubleToVisibilityConverter}"
/>
</ProgressBar.Visibility>
</ProgressBar>
And code:
public class DoubleToVisibilityConvertor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double input = 0;
if (value is double)
{
input = (double)value;
}
else if (value is int)
{
input = (int)value;
}
else if (value is string) // Useful if input of converter is written in XAML
{
if (!double.TryParse((string)value, out input))
return Binding.DoNothing;
}
else
{
return Binding.DoNothing;
}
return (input > 0 ? Visibility.Visible : Visibility.Collapsed);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
Disclaimer: written from memory, not compiled or tested.

Categories

Resources