So I'm at a loss to understand this. I have a custom control that uses up/down buttons to increase/decrease the time as displayed on toggle buttons. I have the Content property of the Toggles displaying the correct values of the Hour and Minute properties, and I'm now trying to set up the code-behind to increase the values. According to the VS2010 debugger, it is increasing the value of the Hour property, but it's not changing the content of the Toggles to reflect this. I have the binding mode set to TwoWay, and I'm using {Binding RelativeSource={RelativeSource TemplatedParent}, Path=Hour, Mode=TwoWay} to bind to the value as this is within the generic.xaml file of the custom control. Any ideas on how to make the displayed value update correctly?
XAML: (Style templates removed to save space)
<Style TargetType="{x:Type local:TimePicker}">
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height}" />
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="Transparent"
BorderThickness="1">
<StackPanel x:Name="PART_Root"
Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="{TemplateBinding VerticalAlignment}">
<!--Region Hour Button-->
<ToggleButton x:Name="PART_Hour"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Margin="0"
BorderBrush="Transparent"
BorderThickness="0"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Hour, Mode=TwoWay, BindsDirectlyToSource=True}">
</ToggleButton>
<!--EndRegion-->
<Label HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalAlignment}"
FontSize="14"
Content=":"/>
<!--Region Minute Button-->
<ToggleButton x:Name="PART_Minute"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Margin="0"
BorderBrush="Transparent"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Minute}">
</ToggleButton>
<!--EndRegion-->
<StackPanel x:Name="PART_IncDecPanel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Vertical">
<Grid Height="{Binding ElementName=PART_Hour, Path=ActualHeight}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Viewbox Stretch="Fill" Grid.Row="0">
<!--Region Increase Button-->
<Button x:Name="PART_IncreaseTime"
HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalAlignment}"
BorderBrush="Transparent"
BorderThickness="0"
FontFamily="Marlett"
Foreground="DimGray"
Content="5"
Padding="0"
Click="PART_IncreaseTime_Click">
</Button>
<!--EndRegion-->
</Viewbox >
<Viewbox Stretch="Fill" Grid.Row="1">
<!--Region Decrease Button-->
<Button x:Name="PART_DecreaseTime"
HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalAlignment}"
BorderBrush="Transparent"
BorderThickness="0"
FontFamily="Marlett"
Foreground="DimGray"
Content="6"
Padding="0">
</Button>
<!--EndRegion-->
</Viewbox>
</Grid>
</StackPanel>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Code:
public class TimePicker : Control
{
#region Dependency Property Declarations
public static DependencyProperty HourProperty = DependencyProperty.Register("Hour", typeof(int), typeof(TimePicker),
new FrameworkPropertyMetadata((int)12, new PropertyChangedCallback(OnHourChanged)));
public static DependencyProperty MinuteProperty = DependencyProperty.Register("Minute", typeof(string), typeof(TimePicker),
new FrameworkPropertyMetadata((string)"00", new PropertyChangedCallback(OnMinuteChanged)));
#endregion
#region Properties
public int Hour
{
get { return (int)GetValue(HourProperty); }
set { SetValue(HourProperty, value); }
}
public string Minute
{
get { return (string)GetValue(MinuteProperty); }
set { SetValue(MinuteProperty, value); }
}
#endregion
#region Events
private static void OnHourChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
TimePicker time = new TimePicker();
time.Hour = (int)e.NewValue;
MessageBox.Show("Hour changed");
}
private static void OnMinuteChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
}
#endregion
static TimePicker()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(typeof(TimePicker)));
}
}
public partial class TimePickerEvents : ResourceDictionary
{
TimePicker time = new TimePicker();
void PART_IncreaseTime_Click(object sender, RoutedEventArgs e)
{
time.Hour += 1;
}
}
Not sure why you are using a Togglebutton to display the hour/minutes, I would think a Label/TextBlock would be better suited for that.
Change the binding of your ToggleButtons to
Content="{TemplateBinding Hour}"
Then in the code, override OnApplyTemplate as such
public override void OnApplyTemplate()
{
var upButton = GetTemplateChild("PART_IncreaseTime") as Button;
upButton.Click += IncreaseClick;
var downButton = GetTemplateChild("PART_DecreaseTime") as Button;
downButton.Click += DecreaseClick;
}
private void IncreaseClick(object sender, RoutedEventArgs e)
{
// Here would be a place to see what toggle button is checked
// (or which TextBlock last had focus) and increase Hour/Minute
// based on that info
Hour += 1;
}
private void DecreaseClick(object sender, RoutedEventArgs e)
{
// Here would be a place to see what toggle button is checked
// (or which TextBlock last had focus) and decreaseHour/Minute
// based on that info
Hour -= 1;
}
This should get you started.
FYI: Within your OnHourChanged and OnMinuteChanged the sender is your control (TimePicker). So you can cast the sender to TimePicker and access all of your properties. Even your private properties.
Why don't you use the WPF toolkit's numerical up down control?
http://wpftoolkit.codeplex.com/wikipage?title=NumericUpDown
All you have to do is bind it to your properties two way.
Related
I merged the things I have found out to implement an auto-scroll-to-end Headered Items Control. I cannot manage to do it. What am I doing wrong?
In Resource Directory, ScrollingHeaderedItemsControl is styled as:
<Style TargetType="common:ScrollingHeaderedItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedItemsControl}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="3" SnapsToDevicePixels="True">
<StackPanel>
<Grid>
<Rectangle Fill="{TemplateBinding Background}"/>
<ContentPresenter ContentSource="Header" Margin="2,0,0,0"/>
</Grid>
<ScrollViewer VerticalScrollBarVisibility="Hidden">
<ItemsPresenter Margin="5,0,0,0"/>
</ScrollViewer>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The class ScrollingHeaderedItemsControl is defined as here:
public class ScrollingHeaderedItemsControl : HeaderedItemsControl
{
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
int newItemCount = e.NewItems.Count;
if (newItemCount > 0)
this.ScrollToEnd();
base.OnItemsChanged(e);
}
}
}
ScrollToEnd is a static function written specifically for ScrollingHeaderedItemsControl such as:
public static void ScrollToEnd(this ItemsControl control)
{
try
{
Border border = VisualTreeHelper.GetChild((DependencyObject)control, 0) as Border;
StackPanel sp = VisualTreeHelper.GetChild((DependencyObject)border, 0) as StackPanel;
ScrollViewer sv = VisualTreeHelper.GetChild((DependencyObject)sp, 1) as ScrollViewer;
sv.ScrollToEnd();
}
catch(Exception)
{
}
}
ScrollingHeaderedItemsControl is used in the UserControl like this:
<common:ScrollingHeaderedItemsControl x:Name="MessagesHIC" FontSize="32" Header="Error/Warning/Info Messages"
Background="Green"
BorderBrush="AntiqueWhite" ItemsSource="{Binding Messages}"
Margin="10" Grid.Row="1" Grid.Column="0"
ScrollViewer.CanContentScroll="True">
<common:ScrollingHeaderedItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding MessageString}" Foreground="{Binding MessageColor}" TextWrapping="Wrap" FontWeight="Light" FontSize="26" />
</DataTemplate>
</common:ScrollingHeaderedItemsControl.ItemTemplate
</common:ScrollingHeaderedItemsControl>
Your template contains a stackpanel which doesn't set a height constraint, therefore the scrollviewer is never required to show the scrollbars. Change your Template to the following:
<Style TargetType="common:ScrollingHeaderedItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedItemsControl}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="3" SnapsToDevicePixels="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid>
<Rectangle Fill="{TemplateBinding Background}" />
<ContentPresenter ContentSource="Header" Margin="2,0,0,0" />
</Grid>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Hidden">
<ItemsPresenter Margin="5,0,0,0" />
</ScrollViewer>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You will then need to change your code behind to:
try
{
Border border = VisualTreeHelper.GetChild((DependencyObject)control, 0) as Border;
Grid sp = VisualTreeHelper.GetChild((DependencyObject)border, 0) as Grid;
ScrollViewer sv = VisualTreeHelper.GetChild((DependencyObject)sp, 1) as ScrollViewer;
sv.ScrollToEnd();
}
catch (Exception)
{
}
I've using Microsoft InteractiveDataDisplay.WPF (former DynamicDataDisplay) to visualize real time data (about 2-3 seconds).
This code xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
double[] y = new double[200];
double[] x = new double[200];
for (int i = 0; i < 200; i++)
{
y[i] = 3.1415 * i / (y.Length - 1);
x[i] = DateTime.Now.AddMinutes(-i).ToOADate();
}
linegraph.Plot(x, y);
}
}
with this xaml:
<d3:Chart Name="plotter">
<d3:Chart.Title>
<TextBlock HorizontalAlignment="Center" FontSize="18" Margin="0,5,0,5">chart sample</TextBlock>
</d3:Chart.Title>
<d3:LineGraph x:Name="linegraph" Description="Simple linegraph" Stroke="Blue" StrokeThickness="3">
</d3:LineGraph>
</d3:Chart>
Give this view:
But I want following custom graph:
Any ideas how to did it? Thanks!
Update 1 (using Kevin Ross solution):
Update 2 (using Dmitry Voytsekhovskiy solution):
But the time axis (Y) not synchronize and not move with data. how to fix this?
How to move axis to the top?
The chart layout template is defined in Themes/Generic.xaml as a style for d3:Chart.
You can create a custom style where the horizontal axis is located at the top (d3:Figure.Placement="Top") and has correct orientation (AxisOrientation="Top"). For instance,
<d3:PlotAxis x:Name="PART_horizontalAxis"
d3:Figure.Placement="Top"
AxisOrientation="Top"
Foreground="{TemplateBinding Foreground}">
<d3:MouseNavigation IsVerticalNavigationEnabled="False"/>
</d3:PlotAxis>
How to use custom formatting for axis labels?
For example, if values along y are actually hours since certain time moment, and you want to show axis ticks as HH:mm, you need to inject a custom label provider into an axis control.
To do that you can create new axis class derived from Axis and pass custom label provider to the base constructor:
public class CustomLabelProvider : ILabelProvider
{
public static DateTime Origin = new DateTime(2000, 1, 1);
public FrameworkElement[] GetLabels(double[] ticks)
{
if (ticks == null)
throw new ArgumentNullException("ticks");
List<TextBlock> Labels = new List<TextBlock>();
foreach (double tick in ticks)
{
TextBlock text = new TextBlock();
var time = Origin + TimeSpan.FromHours(tick);
text.Text = time.ToShortTimeString();
Labels.Add(text);
}
return Labels.ToArray();
}
}
public class CustomAxis : Axis
{
public CustomAxis() : base(new CustomLabelProvider(), new TicksProvider())
{
}
}
Now return to the custom Chart template and change for the vertical axis its type from PlotAxis to CustomAxis (note that you might need to change type prefix):
<d3:CustomAxis x:Name="PART_verticalAxis"
d3:Figure.Placement="Left"
AxisOrientation="Left"
Foreground="{TemplateBinding Foreground}">
<d3:MouseNavigation IsHorizontalNavigationEnabled="False"/>
</d3:CustomAxis>
If we do the described steps for the LineGraphSample and run it, we get the following:
Finally, the custom chart style:
<Style TargetType="d3:Chart">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="d3:Chart">
<Grid>
<d3:Figure x:Name="PART_figure" Margin="1"
PlotHeight="{Binding PlotHeight, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
PlotWidth="{Binding PlotWidth, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
PlotOriginX="{Binding PlotOriginX, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
PlotOriginY="{Binding PlotOriginY, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsAutoFitEnabled="{Binding IsAutoFitEnabled, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
AspectRatio="{Binding AspectRatio, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
ExtraPadding="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<d3:MouseNavigation IsVerticalNavigationEnabled="{TemplateBinding IsVerticalNavigationEnabled}"
IsHorizontalNavigationEnabled="{TemplateBinding IsHorizontalNavigationEnabled}"
x:Name="PART_mouseNavigation"/>
<d3:KeyboardNavigation IsVerticalNavigationEnabled="{TemplateBinding IsVerticalNavigationEnabled}"
IsHorizontalNavigationEnabled="{TemplateBinding IsHorizontalNavigationEnabled}"
x:Name="PART_keyboardNavigation"/>
<d3:VerticalContentControl d3:Figure.Placement="Left"
Content="{TemplateBinding LeftTitle}"
VerticalAlignment="Center"
IsTabStop="False"/>
<d3:CustomAxis x:Name="PART_verticalAxis"
d3:Figure.Placement="Left"
AxisOrientation="Left"
Foreground="{TemplateBinding Foreground}">
<d3:MouseNavigation IsHorizontalNavigationEnabled="False"/>
</d3:CustomAxis>
<d3:AxisGrid x:Name="PART_axisGrid"
VerticalTicks="{Binding Ticks,ElementName=PART_verticalAxis, Mode=OneWay}"
HorizontalTicks="{Binding Ticks,ElementName=PART_horizontalAxis, Mode=OneWay}"
Stroke="{TemplateBinding Foreground}" Opacity="0.25"/>
<ContentControl d3:Figure.Placement="Top"
HorizontalAlignment="Center"
FontSize="16"
Content="{TemplateBinding Title}"
Foreground="{TemplateBinding Foreground}"
IsTabStop="False"/>
<ContentControl d3:Figure.Placement="Bottom"
HorizontalAlignment="Center"
Content="{TemplateBinding BottomTitle}"
Foreground="{TemplateBinding Foreground}"
IsTabStop="False"/>
<d3:VerticalContentControl d3:Figure.Placement="Right"
Content="{TemplateBinding RightTitle}"
VerticalAlignment="Center"
IsTabStop="False"/>
<d3:PlotAxis x:Name="PART_horizontalAxis"
d3:Figure.Placement="Top"
AxisOrientation="Top"
Foreground="{TemplateBinding Foreground}">
<d3:MouseNavigation IsVerticalNavigationEnabled="False"/>
</d3:PlotAxis>
<ContentPresenter/>
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding Foreground}" d3:Figure.Placement="Center"/>
<d3:Legend x:Name="PART_legend"
Foreground="Black" Content="{TemplateBinding LegendContent}"
Visibility="{TemplateBinding LegendVisibility}"/>
</d3:Figure>
<Rectangle x:Name="FocusVisualElement" RadiusX="2" RadiusY="2" Stroke="#FF6DBDD1" StrokeThickness="1" Opacity="0" IsHitTestVisible="false" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsTabStop" Value="False"/>
</Style>
I managed to improve the solution suggested by Dmitry so that the axis remains linked to the graph.
<Style x:Key="timeAxisStyle" TargetType="d3:PlotAxis">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="d3:PlotAxis">
<Grid>
<local:CustomAxis x:Name="PART_Axis"
AxisOrientation="{Binding AxisOrientation, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsReversed="{Binding IsReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Ticks="{Binding Ticks, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Foreground="{Binding Foreground, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
<ContentPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsTabStop" Value="False"/>
</Style>
<Style TargetType="d3:Chart">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="d3:Chart">
<Grid>
<d3:Figure x:Name="PART_figure" Margin="1"
PlotHeight="{Binding PlotHeight, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
PlotWidth="{Binding PlotWidth, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
PlotOriginX="{Binding PlotOriginX, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
PlotOriginY="{Binding PlotOriginY, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsXAxisReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsYAxisReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsAutoFitEnabled="{Binding IsAutoFitEnabled, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
AspectRatio="{Binding AspectRatio, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
ExtraPadding="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<d3:MouseNavigation IsVerticalNavigationEnabled="{TemplateBinding IsVerticalNavigationEnabled}"
IsHorizontalNavigationEnabled="{TemplateBinding IsHorizontalNavigationEnabled}"
x:Name="PART_mouseNavigation"/>
<d3:KeyboardNavigation IsVerticalNavigationEnabled="{TemplateBinding IsVerticalNavigationEnabled}"
IsHorizontalNavigationEnabled="{TemplateBinding IsHorizontalNavigationEnabled}"
x:Name="PART_keyboardNavigation"/>
<d3:VerticalContentControl d3:Figure.Placement="Left"
Content="{TemplateBinding LeftTitle}"
VerticalAlignment="Center"
IsTabStop="False"/>
<d3:PlotAxis x:Name="PART_verticalAxis"
d3:Figure.Placement="Left"
AxisOrientation="Left"
IsReversed = "{Binding IsYAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Foreground="{TemplateBinding Foreground}"
Style="{StaticResource timeAxisStyle}">
<d3:MouseNavigation IsHorizontalNavigationEnabled="False"/>
</d3:PlotAxis>
<d3:AxisGrid x:Name="PART_axisGrid"
VerticalTicks="{Binding Ticks,ElementName=PART_verticalAxis, Mode=OneWay}"
HorizontalTicks="{Binding Ticks,ElementName=PART_horizontalAxis, Mode=OneWay}"
IsXAxisReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsYAxisReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Stroke="{TemplateBinding Foreground}" Opacity="0.25"/>
<ContentControl d3:Figure.Placement="Top"
HorizontalAlignment="Center"
FontSize="16"
Content="{TemplateBinding Title}"
Foreground="{TemplateBinding Foreground}"
IsTabStop="False"/>
<ContentControl d3:Figure.Placement="Bottom"
HorizontalAlignment="Center"
Content="{TemplateBinding BottomTitle}"
Foreground="{TemplateBinding Foreground}"
IsTabStop="False"/>
<d3:VerticalContentControl d3:Figure.Placement="Right"
Content="{TemplateBinding RightTitle}"
VerticalAlignment="Center"
IsTabStop="False"/>
<d3:PlotAxis x:Name="PART_horizontalAxis"
d3:Figure.Placement="Bottom"
AxisOrientation="Bottom"
IsReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Foreground="{TemplateBinding Foreground}">
<d3:MouseNavigation IsVerticalNavigationEnabled="False"/>
</d3:PlotAxis>
<ContentPresenter/>
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding Foreground}" d3:Figure.Placement="Center"/>
<d3:Legend x:Name="PART_legend"
Foreground="Black" Content="{TemplateBinding LegendContent}"
Visibility="{TemplateBinding LegendVisibility}"/>
</d3:Figure>
<Rectangle x:Name="FocusVisualElement" RadiusX="2" RadiusY="2" Stroke="#FF6DBDD1" StrokeThickness="1" Opacity="0" IsHitTestVisible="false" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsTabStop" Value="False"/>
</Style>
This is what I came up with, it's quite rough round the edges but should help you on your way. Your view XAML stays largely the same I've just added a button to start and stop things
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Click="Button_Click" Content="GO"/>
<d3:Chart Name="plotter" Grid.Row="1">
<d3:Chart.Title>
<TextBlock HorizontalAlignment="Center" FontSize="18" Margin="0,5,0,5">chart sample</TextBlock>
</d3:Chart.Title>
<d3:LineGraph x:Name="linegraph" Description="Simple linegraph" Stroke="Blue" StrokeThickness="3">
</d3:LineGraph>
</d3:Chart>
</Grid>
Your code behind then becomes
public partial class LiveView : Window
{
private const int DataPointsToShow = 100;
public Tuple<LinkedList<double>, LinkedList<double>> GraphData = new Tuple<LinkedList<double>, LinkedList<double>>(new LinkedList<double>(), new LinkedList<double>());
public Timer GraphDataTimer;
public LiveView()
{
InitializeComponent();
GraphDataTimer = new Timer(50);
GraphDataTimer.Elapsed += GraphDataTimer_Elapsed;
}
private void GraphDataTimer_Elapsed(object sender, ElapsedEventArgs e)
{
Random random = new Random();
if (GraphData.Item1.Count() > DataPointsToShow)
{
GraphData.Item1.RemoveFirst();
GraphData.Item2.RemoveFirst();
}
GraphData.Item1.AddLast(random.NextDouble()*200);
GraphData.Item2.AddLast(DateTime.Now.ToOADate());
Dispatcher.Invoke(() =>
{
linegraph.Plot(GraphData.Item1, GraphData.Item2);
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (GraphDataTimer.Enabled)
{
GraphDataTimer.Stop();
}
else
{
GraphDataTimer.Start();
}
}
}
Basically what it does it come up with a new value every 50 milliseconds and adds it to the end of the linked list. If the total number of points is above the number you want to display then it also removes the first one giving you a constantly scrolling graph with the most recent data at the top.
I'm trying to create a download bar like chrome.
The issue I'm currently having is trying to bind the click event to the button's context menu within the listboxitem. When the context menuitem is clicked, it says the action is not found.
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Button BorderBrush="Transparent" BorderThickness="0" telerik:StyleManager.Theme="Windows8" Click="ButtonBase_OnClick">
<StackPanel Name="Panel" SnapsToDevicePixels="True"
Orientation="Horizontal" Margin="1 0"
Height="30">
<ContentControl Margin="0 0 10 0" Height="20">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate" Value="{StaticResource Icons.File}"></Setter>
</Style>
</ContentControl.Style>
</ContentControl>
<TextBlock Foreground="Black" Text="{Binding FileName}"
VerticalAlignment="Center"
TextAlignment="Center"
Margin="1 0 0 0"/>
<Button x:Name="ExpandButton" Background="Transparent" Click="ExpandButton_OnClick" BorderThickness="0" VerticalAlignment="Center" ContextMenuService.IsEnabled="false">
<Button.ContextMenu>
<ContextMenu x:Name="popup">
<MenuItem Header="Open" cal:Message.Attach="[Click] = [Open($this)]"></MenuItem>
</ContextMenu>
</Button.ContextMenu>
<ContentControl ContentTemplate="{StaticResource Icons.ArrowUp}" Width="10" Height="10" Margin="2" VerticalAlignment="Center"/>
</Button>
<Rectangle Width="2" Fill="Gray" Margin="0 0 0 0"/>
</StackPanel>
</Button>
</ControlTemplate>
I could bind it behind code(xaml.cs) side of the application but I also lose track of what item is the context suppose to point to. To do that, i replaced caliburn's click event with a regular Click event. The SelectedItem and SelectedItems is null or empty, respectively.
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
var originalSource = e.OriginalSource;
var selectedItem = FileListBox.SelectedItem;
var SelectedItems = FileListBox.SelectedItems;
}
Haven't tested but something along these lines should open the context menu on right or left click:
<Button x:Name="ExpandButton" Background="Transparent" Click="ContextMenu_Click" BorderThickness="0" VerticalAlignment="Center" ContextMenuService.IsEnabled="false">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu x:Name="popup" MenuItem.Click="menuItem_Click">
<MenuItem Header="Open" cal:Message.Attach="[Click] = [Open($this)]"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
<ContentControl ContentTemplate="{StaticResource Icons.ArrowUp}" Width="10" Height="10" Margin="2" VerticalAlignment="Center"/>
</Button>
As for the code-behind, the following worked for me in my last tug with a similar issue:
DependencyObject mainDep = new DependencyObject();
private void ContextMenu_Click(object sender, RoutedEventArgs e)
{
DependencyObject dep = (DependencyObject)e.OriginalSource;
while ((dep != null) && !(dep is ListBoxItem))
{
dep = VisualTreeHelper.GetParent(dep);
}
mainDep = dep;
}
private void menuItem_Click(object sender, RoutedEventArgs e)
{
DependencyObject dep = mainDep;
if (dep is ListBoxItem)
{
...
DO your stuff here
...
}
}
Let me know how these work for you
I have a password box, but i also have a textblock as hint text within the control template. I'd like this to be removed when the password box has a value. I have tried this below but it doesn't work, how can I do this?
Simplified XAML :
<PasswordBox Height="20" Name="pwdBox" PasswordChanged="pwdBox_PasswordChanged" Style="{DynamicResource PasswordBoxStyle1}"/>
<Style x:Key="PasswordBoxStyle1" TargetType="{x:Type PasswordBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border x:Name="Border" .. >
<StackPanel ..>
<TextBlock x:Name="LabelTextBlock" ...
Text="Password Label" />
<Grid>
<ScrollViewer x:Name="PART_ContentHost"
Focusable="false"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"/>
<TextBlock x:Name="HintTextBlock"
Focusable="False"
IsHitTestVisible="False"
Opacity="0"
Text="Enter Your Password" />
</Grid>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Code Behind :
private void pwdBox_PasswordChanged(object sender, RoutedEventArgs e)
{
if (pwdBox.SecurePassword.Length == 0)
{
HintTextBlock.IsVisible = true;
}
else
{
HintTextBlock.IsVisible = false;
}
}
It says that the name 'HintTextBlock does not exist in the current context'
Since, the text box HintTextBlock is part of Template of PassworkBox so it can not accessed directly as it is not part of direct control of window. Use the FindName to find the control in template of passwordbox.
TextBlock hintTextBlock = pwdBox.Template.FindName("HintTextBlock", pwdBox) as TextBlock;
if (pwdBox.SecurePassword.Length == 0)
{
hintTextBlock.Visiblility = Visiblitity.Visible;
}
else
{
hintTextBlock.Visiblility = Visiblility.Collapsed;
}
I need a thumb control that can be sized using a mouse. When the user hovers the mouse over one of the ends a size cursor should be displayed and when the user clicks and drags the end of the control it will be re-sized.
How can that be achieved?
Here is one I made a while ago, it allows Move and Resize, but you can remove the Move logic and it should work fine (the style is still a bit messy, but it works pretty well)
Its based on ContentControl so you can add any Element inside and Move/Resize on a Canvas, It uses 3 Adorners, one for Resize, one for Move and one to display information (current size)
Here is a full working example if you want to test/use/modify/improve :)
Code:
namespace WpfApplication21
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class ResizeThumb : Thumb
{
public ResizeThumb()
{
DragDelta += new DragDeltaEventHandler(this.ResizeThumb_DragDelta);
}
private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control designerItem = this.DataContext as Control;
if (designerItem != null)
{
double deltaVertical, deltaHorizontal;
switch (VerticalAlignment)
{
case VerticalAlignment.Bottom:
deltaVertical = Math.Min(-e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
designerItem.Height -= deltaVertical;
break;
case VerticalAlignment.Top:
deltaVertical = Math.Min(e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + deltaVertical);
designerItem.Height -= deltaVertical;
break;
default:
break;
}
switch (HorizontalAlignment)
{
case HorizontalAlignment.Left:
deltaHorizontal = Math.Min(e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + deltaHorizontal);
designerItem.Width -= deltaHorizontal;
break;
case HorizontalAlignment.Right:
deltaHorizontal = Math.Min(-e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
designerItem.Width -= deltaHorizontal;
break;
default:
break;
}
}
e.Handled = true;
}
}
public class MoveThumb : Thumb
{
public MoveThumb()
{
DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta);
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control designerItem = this.DataContext as Control;
if (designerItem != null)
{
double left = Canvas.GetLeft(designerItem);
double top = Canvas.GetTop(designerItem);
Canvas.SetLeft(designerItem, left + e.HorizontalChange);
Canvas.SetTop(designerItem, top + e.VerticalChange);
}
}
}
public class SizeAdorner : Adorner
{
private Control chrome;
private VisualCollection visuals;
private ContentControl designerItem;
protected override int VisualChildrenCount
{
get
{
return this.visuals.Count;
}
}
public SizeAdorner(ContentControl designerItem)
: base(designerItem)
{
this.SnapsToDevicePixels = true;
this.designerItem = designerItem;
this.chrome = new Control();
this.chrome.DataContext = designerItem;
this.visuals = new VisualCollection(this);
this.visuals.Add(this.chrome);
}
protected override Visual GetVisualChild(int index)
{
return this.visuals[index];
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
this.chrome.Arrange(new Rect(new Point(0.0, 0.0), arrangeBounds));
return arrangeBounds;
}
}
}
Xaml:
<Window x:Class="WpfApplication21.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication21"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<local:MoveThumb Cursor="SizeAll">
<local:MoveThumb.Style>
<Style TargetType="{x:Type local:MoveThumb}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MoveThumb}">
<Rectangle Fill="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:MoveThumb.Style>
</local:MoveThumb>
<Control x:Name="resizer">
<Control.Style>
<Style TargetType="{x:Type Control}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<Grid>
<Grid Opacity="0" Margin="-3">
<local:ResizeThumb Height="3" Cursor="SizeNS" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<local:ResizeThumb Width="3" Cursor="SizeWE" VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
<local:ResizeThumb Width="3" Cursor="SizeWE" VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
<local:ResizeThumb Height="3" Cursor="SizeNS" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
<local:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNWSE" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<local:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNESW" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<local:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNESW" VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<local:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNWSE" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
</Grid>
<Grid IsHitTestVisible="False" Opacity="1" Margin="-3">
<Grid.Resources>
<Style TargetType="{x:Type Ellipse}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Stroke" Value="#FFC8C8C8" />
<Setter Property="StrokeThickness" Value=".5" />
<Setter Property="Width" Value="7" />
<Setter Property="Height" Value="7" />
<Setter Property="Margin" Value="-2" />
<Setter Property="Fill" Value="Silver" />
</Style>
</Grid.Resources>
<Rectangle SnapsToDevicePixels="True" StrokeThickness="1" Margin="1" Stroke="Black" StrokeDashArray="4 4"/>
<Ellipse HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Ellipse HorizontalAlignment="Right" VerticalAlignment="Top"/>
<Ellipse HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
<Ellipse HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Control.Style>
</Control>
<Grid x:Name="sizeInfo" SnapsToDevicePixels="True">
<Path Stroke="Red" StrokeThickness="1" Height="10" VerticalAlignment="Bottom" Margin="-2,0,-2,-15" Stretch="Fill" Data="M0,0 0,10 M 0,5 100,5 M 100,0 100,10"/>
<TextBlock Text="{Binding Width}" Background="White" Padding="3,0,3,0" Foreground="Red" Margin="0,0,0,-18" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
<Path Stroke="Red" StrokeThickness="1" Width="10" HorizontalAlignment="Right" Margin="0,-2,-15,-2" Stretch="Fill" Data="M5,0 5,100 M 0,0 10,0 M 0,100 10,100"/>
<TextBlock Text="{Binding Height}" Background="White" Foreground="Red" Padding="3,0,3,0" Margin="0,0,-18,0" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90" CenterX="1" CenterY="0.5"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
<ContentPresenter Content="{TemplateBinding Content}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="sizeInfo" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="sizeInfo" Property="Visibility" Value="Hidden" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Canvas>
<ContentControl Width="183" Height="110" Canvas.Left="166" Canvas.Top="50" />
</Canvas>
</Window>
Result:
With content inside (Button)
Sorry the cursors do not show when using SnipTool