I'm trying to make KinectColorViewer to work with SDK 1.7 but without success. I can display video picture only if I manually copy pixels from camera to Image element.
I have the following XAML:
<Page
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:k="http://schemas.microsoft.com/kinect/2013"
xmlns:WpfViewers="clr-namespace:Microsoft.Samples.Kinect.WpfViewers;assembly=Microsoft.Samples.Kinect.WpfViewers" x:Class="KinectD.Camera"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500"
Title="Camera">
<Grid>
<k:KinectUserViewer k:KinectRegion.KinectRegion="{Binding ElementName=kinectRegion}" Height="100" HorizontalAlignment="Center" VerticalAlignment="Top" />
<k:KinectSensorChooserUI HorizontalAlignment="Center" VerticalAlignment="Top" x:Name="sensorChooserUi" />
<k:KinectRegion x:Name="kinectRegion">
<Grid>
<k:KinectCircleButton Label="Menu" HorizontalAlignment="Right" Height="200" VerticalAlignment="Top" Click="MenuButtonOnClick" >
<StackPanel>
<Image Source="Images/smile.png" Height="30"/>
</StackPanel>
</k:KinectCircleButton>
</Grid>
</k:KinectRegion>
<WpfViewers:KinectColorViewer HorizontalAlignment="Left" Height="240" Margin="10,10,0,0" VerticalAlignment="Top" Width="320" Kinect="{Binding ElementName=sensorChooserUi, Mode=OneWay, Path=Kinect}"/>
</Grid>
And XAML.CS:
public partial class Camera : Page
{
#region "Kinect"
private KinectSensorChooser sensorChooser;
#endregion
public Camera()
{
this.InitializeComponent();
// initialize the sensor chooser and UI
this.sensorChooser = new KinectSensorChooser();
//Assign the sensor chooser with the sensor chooser from the mainwindow.
//We are reusing the sensorchoosing declared in the first window that can in contact with kinect
this.sensorChooser = Generics.GlobalKinectSensorChooser;
//subscribe to the sensorChooserOnKinectChanged event
this.sensorChooser.KinectChanged += SensorChooserOnKinectChanged;
//Assign Kinect Sensorchooser to the sensorchooser we got from our static class
this.sensorChooserUi.KinectSensorChooser = sensorChooser;
// Bind the sensor chooser's current sensor to the KinectRegion
var regionSensorBinding = new Binding("Kinect") { Source = this.sensorChooser };
BindingOperations.SetBinding(this.kinectRegion, KinectRegion.KinectSensorProperty, regionSensorBinding);
}
private void SensorChooserOnKinectChanged(object sender, KinectChangedEventArgs args)
{
bool error = false;
if (args.OldSensor != null)
{
try
{
args.OldSensor.DepthStream.Range = DepthRange.Default;
args.OldSensor.SkeletonStream.EnableTrackingInNearRange = false;
args.OldSensor.DepthStream.Disable();
args.OldSensor.SkeletonStream.Disable();
args.OldSensor.ColorStream.Disable();
}
catch (InvalidOperationException)
{
// KinectSensor might enter an invalid state while enabling/disabling streams or stream features.
// E.g.: sensor might be abruptly unplugged.
error = true;
}
}
if (args.NewSensor != null)
{
try
{
args.NewSensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);
args.NewSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
args.NewSensor.SkeletonStream.Enable();
}
catch (InvalidOperationException)
{
error = true;
// KinectSensor might enter an invalid state while enabling/disabling streams or stream features.
// E.g.: sensor might be abruptly unplugged.
}
}
if (!error)
kinectRegion.KinectSensor = args.NewSensor;
}
private void MenuButtonOnClick(object sender, RoutedEventArgs e)
{
//Unsubscribe to the sensorchooser's event SensorChooseronkinectChanged
this.sensorChooser.KinectChanged -= SensorChooserOnKinectChanged;
(Application.Current.MainWindow.FindName("_mainFrame") as Frame).Source = new Uri("MainMenu.xaml", UriKind.Relative);
}
}
In tutorial I was following (http://channel9.msdn.com/Series/KinectQuickstart/Camera-Fundamentals) instructor just drops KinectColorViewer to a screen, sets path and it's working.
The KinectColorViewer available in the KinectWpfViewers assembly is used in the "Kinect Explorer" example, and that is the best place to see how it is used and behaves. From this example you will find that the proper way to initialize the viewer in the XAML and the bindings that are necessary.
From the code you posted, you appear to binding the Kinect itself (a reference to the hardware) to the KinectColorViewer, which is not what it is expecting. You need to be setting a reference to a KinectSensorManager class, which is part of the KinectWpfViewers assembly.
Here is a simplified XAML with a KinectColorViewer
<Window x:Class="Microsoft.Samples.Kinect.KinectExplorer.KinectWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Microsoft.Samples.Kinect.KinectExplorer"
xmlns:kt="clr-namespace:Microsoft.Samples.Kinect.WpfViewers;assembly=Microsoft.Samples.Kinect.WpfViewers"
Title="Kinect Explorer" Width="812" Height="768">
<Grid>
<kt:KinectColorViewer x:Name="ColorViewer" KinectSensorManager="{Binding KinectSensorManager}" CollectFrameRate="True" RetainImageOnSensorChange="True" />
</Grid>
</Window>
Then you XAML.CS constructor will look something similar to:
public KinectWindow()
{
this.viewModel = new KinectWindowViewModel();
// The KinectSensorManager class is a wrapper for a KinectSensor that adds
// state logic and property change/binding/etc support, and is the data model
// for KinectDiagnosticViewer.
this.viewModel.KinectSensorManager = new KinectSensorManager();
Binding sensorBinding = new Binding("KinectSensor");
sensorBinding.Source = this;
BindingOperations.SetBinding(this.viewModel.KinectSensorManager, KinectSensorManager.KinectSensorProperty, sensorBinding);
// Attempt to turn on Skeleton Tracking for each Kinect Sensor
this.viewModel.KinectSensorManager.SkeletonStreamEnabled = true;
this.DataContext = this.viewModel;
InitializeComponent();
}
Review the "Kinect Explorer" example for complete details.
I had the same problem with using KinectSensorManager. It turns out you need to call to Microsoft.Samples.Kinect.Wpf.Viewers and not just the kinect.dll, kinect.toolkit.dll, and kinect.controls.dll.
However, when I implemented KinectSensorManager, then the KinectUI and SensorChooser stopped working on my xaml interface.
Related
This question already has answers here:
Wait for animation, render to complete - XAML and C#
(2 answers)
In WPF, is there a "render complete" event?
(3 answers)
How to detect when a WPF control has been redrawn?
(2 answers)
Is there a DataGrid "rendering complete" event?
(1 answer)
Showing a WPF loading message until after UI finishes updating
(2 answers)
Closed 2 years ago.
Background:
I have an application that collects data, does calculations and presents them to the user in graphs in a window. For each set of data I take a picture of the window and store it as a .png on the harddrive so that the user can go back and check the result later.
Problem:
Currently, I update the viewmodel with the new data and then have a Task.Delay(...) as to give the application some time to render the new content on the view. But sometimes I will get a picture of the previous dataset if the delay wasn't enough, I can increase the delay time to make it happen less often but that in turn will slow down the program unneccesarilly. I'm basically looking for a smart way to check if the view have been rendered with the new dataset rather than have a dumb delay.
I've looked into Window.ContentRendered event. But that only seems to fire the first time a window is rendered, so I would have to close and re-create a new window for every picture if I want to use that one and that just feels like unneccesary overhead to me. I would need something similar that fires everytime it is re-rendered, or some other way to know if the view is ready for the picture?
Short Answer: Yes, you can do this by calling your picture-saving method on the Dispatcher thread when it is idle by giving it a priority of DispatcherPriority.ApplicationIdle.
Long Answer: Here's a sample showing this at work. I have here an app that updates a viewmodel's text property when you click a button, but it takes a couple of seconds for it to update the control that is bound to it because the text is huge.
The moment I know the new data is trying to be shown, I issue a Dispatcher command to wait for the UI to be idle before I do something:
Dispatcher.Invoke((Action)(() => { // take your picture here }), DispatcherPriority.ApplicationIdle);
MainWindowViewModel.cs
public class MainWindowViewModel : INotifyPropertyChanged
{
private string messages;
private string controlText;
public MainWindowViewModel Parent { get; private set; }
public string Messages { get => this.messages; set { this.messages = value; OnPropertyChanged(); } }
public string ControlText { get => this.controlText; set { this.controlText = value; OnPropertyChanged(); } }
public void UpdateWithNewData()
{
var strBuilder = new StringBuilder();
for (int i = 0; i < 100000; i++)
{
strBuilder.AppendLine($"{DateTime.Now:HH:mm:ss.ffffff}");
}
// This will update the TextBox that is bound to this property,
// but it will take awhile because the text is HUUUUGE.
this.ControlText = strBuilder.ToString();
}
public MainWindowViewModel()
{
this.ControlText = "This area will take a while to render when you click the button below.";
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml
<Window x:Class="_65951670.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Background="LightSalmon">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox IsReadOnly="True" Text="{Binding ControlText,UpdateSourceTrigger=PropertyChanged}" TextWrapping="Wrap" Margin="5" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible"/>
<Button Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="15,5" Content="Update Above With Lots Of Text" Click="Button_Click"/>
</Grid>
<Grid Grid.Row="1">
<TextBox Text="{Binding Messages}" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" Margin="5" IsReadOnly="True"/>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private MainWindowViewModel viewModel;
public MainWindow()
{
InitializeComponent();
viewModel = new MainWindowViewModel();
this.DataContext = viewModel;
this.viewModel.PropertyChanged += ViewModel_PropertyChanged;
}
private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(this.viewModel.ControlText))
{
var sw = new Stopwatch();
sw.Start();
this.viewModel.Messages += $"Property Changed: {DateTime.Now:HH:mm:ss.ffffff}\n";
// If you got here, you know that the DataContext has changed, but you don't know when it will be done rendering.
// So use Dispatcher and wait for it to be idle before performing another action.
// Put your picture-saving method inside of the 'Action' here.
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
{
this.viewModel.Messages += $"UI Became Idle At: {DateTime.Now:HH:mm:ss.ffffff}\nIt took {sw.ElapsedMilliseconds} ms to render, Take Picture Now!";
}));
}
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.viewModel.UpdateWithNewData();
}
}
I'm trying to implement an application that requests weather data from OWM (OpenWeatherMap) and displays the temperature as well as an weather icon fitting the weather outside. OWM provides icon ids for the fitting icon. You can then access those icons over a specific web path. Displaying those icons in my app worked well. Because the icons provided by OWM have got a low resolution I decided to use vector graphics, that I saved in the application directory. Displaying those vector graphics directly using this code
<Image Source="Assets/WeatherIcons/01d.svg" Height="300" HorizontalAlignment="Right"/>
works just fine. But I want to adjust the used source depending on the, by OWM provided, icon id.
My Code at the moment looks like this:
MainPage.xaml:
<Page
x:Class="starting.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:starting"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Black"
PointerPressed="WakeUp">
<StackPanel x:Name="WeatherInformation"
Visibility="Collapsed">
<Image x:Name="WeatherIcon"
Height="300"
HorizontalAlignment="Right"/>
<TextBlock Foreground="White"
TextAlignment="Center">
<Run x:Name="Temperature" FontSize="90"/>
<LineBreak/>
<Run x:Name="WeatherDescription" FontSize="50"/>
</TextBlock>
</StackPanel>
</Grid>
</Page>
MainPage.xaml.cs:
using System;
using System.Globalization;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media.Imaging;
namespace starting
{
public sealed partial class MainPage : Page
{
private DispatcherTimer modeTimer = new DispatcherTimer();
public MainPage()
{
this.InitializeComponent();
updateWeather(this, this);
modeTimer.Tick += new EventHandler<object>(Sleep);
modeTimer.Interval = new TimeSpan(0, 1, 0);
DispatcherTimer updateWeatherTimer = new DispatcherTimer();
updateWeatherTimer.Tick += new EventHandler<object>(updateWeather);
updateWeatherTimer.Interval= new TimeSpan(0, 30, 0);
updateWeatherTimer.Start();
}
private void updateWeather(object sender, object e)
{
WeatherApp.WeatherAPI myWeatherApi = new WeatherApp.WeatherAPI("Friedrichshafen,de");
OpenWeatherMapType.WeatherStream data = myWeatherApi.GetForecast();
WeatherIcon.Source = new BitmapImage(new Uri("ms-appx:///Assets/WeatherIcons/" + data.Weather[0].Icon + ".svg"));
Temperature.Text = Math.Round(data.Main.Temp).ToString() + "°C";
WeatherDescription.Text = data.Weather[0].Description;
}
private void Sleep(object sender, object e)
{
StandByTime.Visibility = Visibility.Visible;
ActiveTime.Visibility = Visibility.Collapsed;
WeatherInformation.Visibility = Visibility.Collapsed;
modeTimer.Stop();
}
private void WakeUp(object sender, PointerRoutedEventArgs e)
{
StandByTime.Visibility = Visibility.Collapsed;
ActiveTime.Visibility = Visibility.Visible;
WeatherInformation.Visibility = Visibility.Visible;
modeTimer.Start();
}
}
}
When I start the application there is no sign of an weather icon showing up. How's that possible?
It seems to be working fine like this:
MainPage.xaml:
<Page>
<Grid>
<Image Height="300">
<Image.Source>
<SvgImageSource x:Name="WeatherIconSource"/>
</Image.Source>
</Image>
</Grid>
</Page>
MainPage.xaml.cs:
private void updateWeather(object sender, object e)
{
WeatherApp.WeatherAPI myWeatherApi = new WeatherApp.WeatherAPI("Friedrichshafen,de");
OpenWeatherMapType.WeatherStream data = myWeatherApi.GetForecast();
WeatherIconSource.UriSource = new Uri("ms-appx:///Assets/WeatherIcons/" + data.Weather[0].Icon + ".svg");
}
I have problem with control Cefsharp ChromiumWebBrowser in WPF. Project contain UserControl and Tabitem which is inside UserControl. When program is running and i move to tabitem control i have exception thrown
An exception of type 'System.Exception' occurred in CefSharp.dll but
was not handled in user code IBrowser instance is null. Browser has
likely not finished initializing or is in the process of disposing.
XAML
<UserControl x:Class="QuoteHubWPF.Controls.ChromeWebBrowser"
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:QuoteHubWPF.Controls"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
xmlns:ge="clr-namespace:SourceChord.ResponsiveGrid;assembly=ResponsiveGrid.Wpf"
mc:Ignorable="d" ClipToBounds="True">
<cefSharp:ChromiumWebBrowser Name="chromiumWebBrowser" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" RenderTransformOrigin="0.5,0.5" Height="360" ClipToBounds="True" Loaded="ChromiumCKEditor_Loaded" IsBrowserInitializedChanged="browser_IsBrowserInitializedChanged" FrameLoadEnd="chromiumWebBrowser_FrameLoadEnd"/>
.cs
public partial class ChromeWebBrowser : UserControl
{
public ChromeWebBrowser()
{
InitializeComponent();
chromiumWebBrowser.Address = "html";
}
public string GetHTML()
{
var htmlFromPage = chromiumWebBrowser.EvaluateScriptAsync("getDataFromTheEditor", 10000);
here exist exception
var response = htmlFromPage.Result;
var result = response.Success ? (response.Result ?? "null").ToString() : response.Message;
return result;
}
private void browser_IsBrowserInitializedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
some code to run script
}
}
Any solution to fix this problem ? Its occurs when run tabitem control.
Update with explained problem
Thank for quickly answer #amaitland. GetHtml is calling in other viewmodel. So how i can initialize once again without error with over process ?
NewJobViewModel
public void GetHTMLFromView()
{
if (View != null)
{
var njView = View as NewJobUC;
var contentJobDescription = njView.JobDescriptionCKEditor.GetHTML();
NewJobUC.xaml
<TabItem ...some code ...
<local:ChromeWebBrowser x:Name="JobScopeCKEditor" CKEContent="{Binding NewJob.JobScope}" HorizontalAlignment="Stretch" Width="{Binding ElementName=canv, Path=ActualWidth}" />
</TabItem>
When MediaElement starts playing video it shows like a black frame for a moment. Here is the code:
<Window x:Class="MediaElementTest.MainWindow" /* */
Title="MainWindow" Height="350" Width="525" Background="Red">
<Grid>
<ContentControl x:Name="contentControl"/>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
NextState();
}
public void NextState()
{
var content = new VideoState();
contentControl.Content = content;
}
}
And
<UserControl x:Class="MediaElementTest.VideoState" /* */
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<MediaElement x:Name="videoPlayer" MediaEnded="videoPlayer_MediaEnded" LoadedBehavior="Manual" />
</Grid>
</UserControl>
public partial class VideoState : UserControl
{
public VideoState()
{
InitializeComponent();
videoPlayer.Source = new Uri("C:\\wpf\\bin\\Debug\\data\\start.mp4");
//videoPlayer.Position = TimeSpan.FromMilliseconds(100); //!!! WORKS FINE WITH IT
videoPlayer.Play();
}
private void videoPlayer_MediaEnded(object sender, RoutedEventArgs e)
{
videoPlayer.Source = null;
videoPlayer = null;
GC.Collect();
MainWindow wnd = (MainWindow)Application.Current.MainWindow;
wnd.NextState();
}
}
If I set videoPlayer.Position to like 100 milliseconds it works fine. How can I get rid of this black frame. I've tried to set ScrubbingEnabled="true"and do something like :
videoPlayer.Play();
videoPlayer.Pause();
videoPlayer.Play();
But there is no difference and black popup still occurs. If I set videoPlayer.Position to 0ms at mediaEnded event and play it works also fine. I would appreciate any help.
Get the Loaded event of your User Control and move the below code in the handler of Loaded event in your User control's code behind file
videoPlayer.Source = new Uri("C:\\wpf\\bin\\Debug\\data\\start.mp4");
videoPlayer.Play();
Once the VideoState user control is properly loaded as the content of your window, only then you execute further logic to play the video. I hope that it will solve the black screen problem.
I have a control which i'm putting in dialog as a content. Due to relization of this dialog i have to create it every time when i need it(Show/Hide won't do the trick). I want my control to remember field content beetween calls. While i can apply viewmodel to achieve this i prefer just keep control as a field and assing it as content of dialog every time i need it. But i run into following error:
"Specified element is already the logical child of another element. Disconnect it first."
I tried to assing null to dialog window's content before closing it, but it doesn't solve the problem. Is there anything i can do?
Setting window.Content = null works fine for me. Following is the code I used:
public partial class MainWindow : Window
{
TextBlock textBlock = new TextBlock();
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
TestWindow testWindow = new TestWindow();
testWindow.Content = textBlock;
testWindow.Closing += HandleTestWindowClosing;
testWindow.Show();
}
void HandleTestWindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
var testWindow = sender as TestWindow;
if(testWindow!=null)
{
testWindow.Content = null;
testWindow.Closing -= HandleTestWindowClosing;
}
}
}
Check out the following working example. It isn't exactly your scenario, but pretty close. The key is setting the 'Child' property to null. It moves the TextBox from the top border to the bottom.
<Window x:Class="SO.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.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Grid.Row="0" Click="Move_Click">Move</Button>
<Border x:Name="topBorder" Grid.Row="1">
<TextBlock x:Name="ctrl">Some Text Block</TextBlock>
</Border>
<Border x:Name="bottomBorder" Grid.Row="2"/>
</Grid>
</Window>
and the code behind:
using System.Windows;
namespace SO
{
public partial class MainWindow :Window
{
public MainWindow( )
{
InitializeComponent( );
}
private void Move_Click( object sender, RoutedEventArgs e )
{
this.topBorder.Child = null;
this.bottomBorder.Child = this.ctrl;
}
}
}