I'm writing my first universal windows app and am currently experimenting with the accelerometer.
Currently I'm displaying it's reading as numbers on the screen but I would like to also show a graph that depicts these values visually.
I've seen some charting examples online but none that accept a live stream of data and display it as it comes in.
Basically what I want is a graph over the time domain that draws a line which represents the values the accelerometer outputs.
This is my first attempt at windows programming and I'm working with C#.
Is there a 'proper' way to do such a thing? a commonly accepted method?
Use WinRTXamlToolkit:
XAML:
<Page
x:Class="App3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Charting="using:WinRTXamlToolkit.Controls.DataVisualization.Charting"
mc:Ignorable="d" Loaded="Page_Loaded">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Charting:Chart x:Name="chart1" Grid.Row="0">
<Charting:LineSeries ItemsSource="{Binding Data}"
DependentValuePath ="Accel"
IndependentValuePath ="Timestamp" Margin="0"/>
</Charting:Chart>
<Button x:Name="btnStart" Content="START" Grid.Row="1" Click="btnStart_Click" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></Button>
<Button x:Name="btnStop" Content="STOP" Grid.Row="2" Click="btnStop_Click" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></Button>
</Grid>
</Page>
MainPage:
public sealed partial class MainPage : Page
{
MyViewModel vm;
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
vm = new MyViewModel();
DataContext = vm;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
vm.Start();
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
vm.Stop();
}
}
ViewModel:
public class MyViewModel
{
private Timer accelerometer;
private Random r;
private ObservableCollection<MyAccelModel> data;
public ObservableCollection<MyAccelModel> Data { get { return data; } }
public MyViewModel()
{
data = new ObservableCollection<MyAccelModel>();
r = new Random(DateTime.Now.Millisecond);
}
public void Start()
{
accelerometer = new Timer(AccelDataCallback, null, 100, 500);
}
public void Stop()
{
accelerometer.Dispose();
}
private async void AccelDataCallback(object state)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
data.Add(new MyAccelModel { Timestamp = DateTime.Now, Accel = r.NextDouble() });
});
}
}
Related
I have a function that listenes to PointerWheelChanged events of my UIElement.
I can retreive the MouseWheelDelta but this doesn't tell me if the mouse wheel was tilted or moved up/down.
How can I get this info?
This is my code to get the delta:
private void TestScrollViewer_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
PointerPoint ptrPt = e.GetCurrentPoint((UIElement)sender);
int delta = ptrPt.Properties.MouseWheelDelta;
// positive: forward/right motion; negative: backward/left motion
}
You can use IsHorizontalMouseWheel inside Pointers. See this example below:
MainWindow.xaml
<Window
x:Class="MouseWheelTests.MainWindow"
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:local="using:MouseWheelTests"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid RowDefinitions="Auto,*">
<StackPanel Grid.Row="0">
<TextBlock x:Name="WheelHorizontal" />
<TextBlock x:Name="WheelVertical" />
</StackPanel>
<Grid
Grid.Row="1"
Background="Transparent"
PointerWheelChanged="Grid_PointerWheelChanged" />
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
namespace MouseWheelTests;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
private int WheelHorizontalValue { get; set; }
private int WheelVerticalValue { get; set; }
private void Grid_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
var properties = e.GetCurrentPoint((UIElement)sender).Properties;
if (properties.IsHorizontalMouseWheel is true)
{
WheelHorizontalValue += properties.MouseWheelDelta;
this.WheelHorizontal.Text = $"Horizontal: {WheelHorizontalValue}";
}
else
{
WheelVerticalValue += properties.MouseWheelDelta;
this.WheelVertical.Text = $"Vertical: {WheelVerticalValue}";
}
}
}
I have a vertical ListView using virtualization, and I subscribe to the ContainerContentChanging event. If I insert items into the top of the list, and attempt to get their transform in the handler for ContainerContentChanging using TransformToVisual(Window.Current.Content), as soon as the list has enough items to start virtualizing, I begin getting values like -15000 for the transforms matrix OffsetX and OffsetY values. Since I'm inserting the item at the top, it's clearly rendering (it has appropriate calculated width/height, etc.), so I'm not sure why the transform would be bad.
Here's some code if it helps. It doesn't get much more basic.
XAML:
<Page
x:Class="TestListView.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestListView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="400"/>
</Grid.RowDefinitions>
<Button
Content="Add Item"
Click="OnAddItemClick"/>
<ListView
Grid.Row="1"
ItemsSource="{x:Bind Items, Mode=OneWay}"
ContainerContentChanging="OnContainerContentChanging"/>
</Grid>
</Page>
Code-Behind:
public sealed partial class MainPage : Page
{
public readonly ObservableCollection<string> Items = new ObservableCollection<string>();
public MainPage()
{
this.InitializeComponent();
Items.Add("Test 2");
Items.Add("Test 1");
Items.Add("Test 0");
}
private void OnAddItemClick(object sender, RoutedEventArgs e)
{
Items.Insert(0, $"Test {Items.Count}");
}
private void OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
var addedItem = args?.ItemContainer;
if (addedItem == null)
return;
var transform = addedItem.TransformToVisual(Window.Current.Content);
}
}
I want to display the kinect status-Connected or disconnected and the Device Connection ID.
Connection ID is getting displayed,but Status is not getting displayed in Textblock
My code is-
mainwindow.xaml.cs-
public partial class MainWindow : Window
{
KinectSensor sensor;
private MainWindowViewModel viewModel;
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
this.viewModel = new MainWindowViewModel();
this.DataContext = this.viewModel;
}
void KinectSensors_StatusChanged(object sender, StatusChangedEventArgs e)
{
switch (e.Status)
{
case KinectStatus.Connected:
txtBlckStatus.Text = "Connected";
break;
case KinectStatus.Disconnected:
txtBlckStatus.Text = "Disconnected";
break;
}
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if (KinectSensor.KinectSensors.Count > 0)
{
this.sensor = KinectSensor.KinectSensors[0];
KinectSensor.KinectSensors.StatusChanged += KinectSensors_StatusChanged;
this.StartSensor();
this.sensor.ColorStream.Enable();
this.sensor.DepthStream.Enable();
this.sensor.SkeletonStream.Enable();
}
else
{
MessageBox.Show("No Sensor Connected!!");
this.Close();
}
}
private void StartSensor()
{
if(this.sensor!=null && !this.sensor.IsRunning)
{
this.sensor.Start();
SetKinectInfo();
}
}
private void StopSensor()
{
if (this.sensor != null && !this.sensor.IsRunning)
{
this.sensor.Stop();
}
}
private void SetKinectInfo()
{
if(this.sensor!=null)
{
this.viewModel.ConnectionID = this.sensor.DeviceConnectionId;
}
}
}
mainwindow.xaml
<Window x:Class="KinectInfoBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Status:"></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="txtBlckStatus"></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Connection ID"></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding ConnectionID}"></TextBlock>
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="Stop" Margin="179,81,179,42" x:Name="StopSensorButton"></Button>
</Grid>
</Grid>
</Window>
mainwindowviewmodel.cs--to display the changing connection id
namespace KinectInfoBox
{
public class MainWindowViewModel:INotifyPropertyChanged
{
private string _connectionIDValue;
public string ConnectionID
{
get { return _connectionIDValue; }
set
{
if(this._connectionIDValue!=value)
{
this._connectionIDValue = value;
this.OnNotifyPropertyChange("ConnectionID");
}
}
}
public void OnNotifyPropertyChange(string propertyName)
{
if(this.PropertyChanged!=null)
{
this.PropertyChanged.Invoke(this,newPropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
The code is perfectly allright,actually the kinect is initially connected with the system,so there is no state changed,but when you unplug the kinect then the status change even is triggered,so you will see the status disconnected in the textblock and when you connect ity again the status connected will get displayed there.
I would like to implement a way to pass messages to the UI from an object with computationally intensive methods in order to inform the user of the status and progress of the computations. While doing this the UI should remain responsive, i.e. the computations are performed on another thread. I've read about delegates, backgroundworkers and so on, but I find them very confusing and have not been able to implement them in my application. Here is a simplified application with the same general idea as my application. The textbox in the UI is here updated after the computationally intensive method is completed:
<Window x:Class="UpdateTxtBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UpdateTxtBox" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="94,112,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" />
<TextBox Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="txtBox" VerticalAlignment="Stretch" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" />
</Grid>
namespace UpdateTxtBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
MyTextProducer txtProducer = new MyTextProducer();
txtProducer.ProduceText();
txtBox.Text = txtProducer.myText;
}
}
}
The computationally intensive class:
namespace UpdateTxtBox
{
public class MyTextProducer
{
public string myText { get; private set; }
public MyTextProducer()
{
myText = string.Empty;
}
public void ProduceText()
{
string txt;
for (int i = 0; i < 10; i++)
{
txt = string.Format("This is line number {0}", i.ToString());
AddText(txt);
Thread.Sleep(1000);
}
}
private void AddText(string txt)
{
myText += txt + Environment.NewLine;
}
}
}
How can a modify this code so that the textbox is updated each time the AddText method is called?
The basic problem here is that you are doing computationally intensive operations on the UI thread, which locks up the UI (as you yourself have figured out). The solution to this is to kick off a separate thread and then update the UI from that. But you are then faced with the problem that only the UI thread is allowed to update the UI. This is solved by using the Dispatcher class, which handles all this icky stuff for you.
This is a nice, fleshed our article on the Dispatcher and how to use it: http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
Note that there are other ways to handle this sort of UI updating with delayed/slow tasks, but I'd say this is a sufficient solution to your problem.
As you are using WPF, I would suggest you use databinding, here is an example implementation of your code:
<Window x:Class="UpdateTxtBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UpdateTxtBox" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="94,112,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" />
<TextBox Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="txtBox" VerticalAlignment="Stretch" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" Text="{Binding Path=myText}" />
</Grid>
</Window>
note that the textbox Content property is now databound.
public partial class MainWindow : Window
{
MyTextProducer txtProducer;
public MainWindow()
{
InitializeComponent();
txtProducer = new MyTextProducer();
this.DataContext = txtProducer;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(txtProducer.ProduceText);
txtBox.Text = txtProducer.myText;
}
}
note the this.DataContext = txtProducer line, this is how you tell the binding where to look for values
public class MyTextProducer : INotifyPropertyChanged
{
private string _myText;
public string myText { get { return _myText; } set { _myText = value; RaisePropertyChanged("myText"); } }
public MyTextProducer()
{
myText = string.Empty;
}
public void ProduceText()
{
string txt;
for (int i = 0; i < 10; i++)
{
txt = string.Format("This is line number {0}", i.ToString());
AddText(txt);
Thread.Sleep(1000);
}
}
private void AddText(string txt)
{
myText += txt + Environment.NewLine;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
MyTextProducer now implements INotifyPropertyChanged, so any changes to the myText property will automatically be reflected in the UI.
I am having a problem in my project. This is my Solution Explorer:
BLA is an example UserControl that is loaded twice into a MainWindow Grid:
<Window x:Class="UserControlWechseln.MainWindow"
xmlns:local="clr-namespace:UserControlWechseln"
Title="MainWindow" Height="428" Width="1195" xmlns:am="http://schemas.amcharts.com/charts/wpf/2009/xaml">
<Grid Height="1000" Width="1000">
<Grid Height="500" HorizontalAlignment="Left" Margin="12,31,0,0" Name="grid1" VerticalAlignment="Top" Width="142">
<local:BLA Margin="-3,-17,-270,17" />
</Grid>
<Grid Height="500" HorizontalAlignment="Right" Margin="0,6,31,0" Name="grid2" VerticalAlignment="Top" Width="402">
<local:BLA />
</Grid>
</Grid>
</Window>
The XAML Code for the BLA UserControl looks like this:
<UserControl x:Class="UserControlWechseln.BLA"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Name="Bla">
<Button Content="House" Height="23" HorizontalAlignment="Left" Margin="64,237,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="ButtonClick" />
<Button Content="Soccer" Height="23" HorizontalAlignment="Left" Margin="169,237,0,0" Name="button2" VerticalAlignment="Top" Width="75" Click="ButtonClick" />
</Grid>
</UserControl>
And the C# Code for the BLA UserControl looks like this:
public partial class BLA : UserControl {
public BLA() {
InitializeComponent();
}
private void ButtonClick(object sender, RoutedEventArgs e) {
Button btn = sender as Button;
Bla.Children.Clear();
if (btn.Content.ToString() == "House") {
Haus uc1 = new Haus();
Bla.Children.Add(uc1);
} else if (btn.Content.ToString() == "Soccer") {
Fussball uc2 = new Fussball();
Bla.Children.Add(uc2);
}
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
}
private void button1_Click(object sender, RoutedEventArgs e) {
}
private void button1_Click_1(object sender, RoutedEventArgs e) {
}
private void button1_Click_2(object sender, RoutedEventArgs e) {
}
private void button2_Click(object sender, RoutedEventArgs e) {
}
}
}
The problem now is that you get this view:
Is it possible to trigger the Home Button once and the Img in displays on both UserControls "BLA"? They obviously are the same UserControl.
Thanks for your help.
They are different instances of your UserControl, they do not know that the other exists. You are going to need to create Properties and Events. I am sure there is probably a better way of it but here is something I threw together to demonstrate( I also renamed some of your classes because I do not have the same Namespaces that you have). Also you are clearing all of the controls out of your UserControl when you change your image.
Edit added complete example
UserControl
namespace WpfApplication1{
/// <summary>
/// Interaction logic for Bla.xaml
/// </summary>
public partial class Bla : UserControl
{
public event RoutedEventHandler changeHaus
{
add { AddHandler(myEvents.changeHausEvent, value); }
remove { RemoveHandler(myEvents.changeHausEvent, value); }
}
public event RoutedEventHandler changeSoccer
{
add { AddHandler(myEvents.changeSoccerEvent, value); }
remove { RemoveHandler(myEvents.changeSoccerEvent, value); }
}
public Bla()
{
InitializeComponent();
}
private void ButtonClick(object sender, RoutedEventArgs e) {
Button btn = sender as Button;
if (btn.Content.ToString() == "House")
{
SetHaus();
RoutedEventArgs ea = new RoutedEventArgs(myEvents.changeHausEvent);
RaiseEvent(ea);
}
else if (btn.Content.ToString() == "Soccer")
{
SetSoccer();
RoutedEventArgs ea = new RoutedEventArgs(myEvents.changeSoccerEvent);
RaiseEvent(ea);
}
}
public void SetHaus()
{
display.Fill = new SolidColorBrush(Colors.Blue);
}
public void SetSoccer()
{
display.Fill = new SolidColorBrush(Colors.Red);
}
}
public static class myEvents
{
public static RoutedEvent changeHausEvent = EventManager.RegisterRoutedEvent("changeHaus",RoutingStrategy.Tunnel,typeof(RoutedEventHandler), typeof(Bla));
public static RoutedEvent changeSoccerEvent = EventManager.RegisterRoutedEvent("changeSoccer", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(Bla));
}
}
UserControl Xaml
<UserControl x:Class="WpfApplication1.Bla"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Name="bla">
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Name="display" Height="223" Grid.Row="0" />
<Button Grid.Row="1" Content="House" Height="23" HorizontalAlignment="Left" Margin="64,25,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="ButtonClick" />
<Button Grid.Row="1" Content="Soccer" Height="23" HorizontalAlignment="Left" Margin="169,25,0,0" Name="button2" VerticalAlignment="Top" Width="75" Click="ButtonClick" />
</Grid>
</UserControl>
Main Window
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void bla1_changeHaus(object sender, RoutedEventArgs e)
{
bla2.SetHaus();
}
private void bla1_changeSoccer(object sender, RoutedEventArgs e)
{
bla2.SetSoccer();
}
private void bla2_changeHaus(object sender, RoutedEventArgs e)
{
bla1.SetHaus();
}
private void bla2_changeSoccer(object sender, RoutedEventArgs e)
{
bla1.SetSoccer();
}
}
}
MainWindow Xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:WpfApplication1">
<Grid>
<my:Bla HorizontalAlignment="Left" Margin="0,12,0,0" x:Name="bla1" VerticalAlignment="Top" changeHaus="bla1_changeHaus" changeSoccer="bla1_changeSoccer" />
<my:Bla HorizontalAlignment="Left" Margin="247,12,0,0" x:Name="bla2" VerticalAlignment="Top" changeHaus="bla2_changeHaus" changeSoccer="bla2_changeSoccer" />
</Grid>
</Window>