I have an unsafe class that generate a Bitmap which is converted to ToImageSource in order to draw into a Window. The Bitmap itself contains a sinusoidal text which is frequently updated and I want to it "move" from the left to the right (marquee style?). Anyway it works just fine in a WinForm but I'm stuck with the WPF Window.
Here are some code samples:
public AboutWindow()
{
InheritanceBehavior = InheritanceBehavior.SkipAllNow;
InitializeComponent();
Initialize();
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawImage(bitmapRender.WindowBitmap, drawingArea);
if (!worker.IsBusy)
worker.RunWorkerAsync(); // BackgroundWorker in charge of updating the bitmap
}
void DispatcherTimerRender_Tick(object sender, EventArgs e) => InvalidateVisual();
My issues are: there is nothing displayed on the Window and the DispatchedTimer that calls InvalidateVisual() leads to this exception:
System.InvalidOperationException: 'Cannot use a DependencyObject that belongs to a different thread than its parent Freezable.'
I have looked at other threads and I'm aware that WPF is a retained drawing system but I would love to achieve it anyway.
Any suggestion about the "best" way to achieve this?
Any useful explanation/link would be very much appreciated.
[Edit]
<Window x:Class="CustomerManagement.View.AboutWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Height="450" WindowStartupLocation="CenterScreen" Width="800" ResizeMode="NoResize" AllowsTransparency="True" WindowStyle="None">
<Grid KeyDown="Grid_KeyDown">
<Image Width="800" Height="450" Source="{Binding 'Image'}" />
</Grid>
</Window>
You should use an Image element that has its Source property bound to an ImageSource property in a view model. This is the "standard" way, based on the MVVM architectural pattern, and therefore the "best" way - in my opinion.
<Image Source="{Binding Image}"/>
The view model could look like this:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ImageSource image;
public ImageSource Image
{
get { return image; }
set
{
image = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Image)));
}
}
}
and an instance of it would be assigned to the DataContext of the window:
public AboutWindow()
{
InitializeComponent();
var vm = new ViewModel();
DataContext = vm;
}
For testing it, the code below performs a slide show of image files in a directory. You may as well assign any other ImageSource - e.g. a DrawingImage - to the Image property.
var imageFiles = Directory.GetFiles(..., "*.jpg");
var index = -1;
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
timer.Tick += (s, e) =>
{
if (++index < imageFiles.Length)
{
vm.Image = new BitmapImage(new Uri(imageFiles[index]));
}
else
{
timer.Stop();
}
};
timer.Start();
Related
I have situation where I want an overlay control to block UI interactions on a Page for everything that is behind a border. I have tried setting Border.ManipulationMode to False. I have set IsTapEnabled, IsRightTapEnabled, IsDoubleTapEnabled, and IsHitTestVisible to False.
I also tried subscribing to the Tapped and PointerEntered events, and setting the args Handled property to true. After all of this I can still click on Buttons through the border, and invoke their commands. Below are a few screenshots for context:
Page with no overlay
Page now has what should be an overlay that blocks controls behind it
A button capturing PointerOver that shouldn't be
Here is the UserControl xaml that becomes the overaly on the Page:
<UserControl x:Class="PocMvvmToolkitApp.Dialogs.DialogShell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid x:Name="overlayGrid"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<!--dialogShield is the Border that I want to prevent click through on-->
<Border x:Name="dialogShield"
Background="#AAFFFFFF"
IsHitTestVisible="False"
ManipulationMode="None"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsDoubleTapEnabled="False"
IsHoldingEnabled="False"
IsRightTapEnabled="False"
IsTapEnabled="False"/>
<Border x:Name="dialogBorder"
BorderBrush="Black"
BorderThickness="1" />
</Grid>
Attempting to handle the events:
public DialogShell()
{
this.InitializeComponent();
this.allDialogs = new List<ExtendedContentDialog>();
this.visibleDialogs = new List<ExtendedContentDialog>();
////Doesn't work
this.dialogShield.PointerEntered += this.OnModalShieldPointerEntered;
this.dialogShield.Tapped += this.OnModalShieldTapped;
}
private void OnModalShieldTapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
////Doesn't block click through
e.Handled = true;
}
private void OnModalShieldPointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
e.Handled = true;
}
On the Page.xaml.cs here is where I add or remove the DialogShell control to the parent Grid on the page:
private void OnDialogStackChanged(Args.DialogStackChangedEventArgs args)
{
switch (args.Context)
{
case Args.DialogStackChangedContext.Showing:
if (this.dialogShell == null)
{
this.dialogShell = new DialogShell();
this.dialogShell.ShowDialog(args.Dialog);
this.rootGrid.Children.Add(this.dialogShell);
Grid.SetColumnSpan(this.dialogShell, 2);
}
break;
case Args.DialogStackChangedContext.Closing:
if (this.dialogShell != null)
{
this.dialogShell.RemoveDialog(args.Dialog);
if (this.dialogShell.AllDialogs.Count == 0)
{
this.rootGrid.Children.Remove(this.dialogShell);
this.dialogShell = null;
}
}
break;
}
}
Any help with this Border situation would be appreciated. Before someone recommends using ContentDialog, please don't, I have my reasons for this setup. Thanks!
Yesterday I posted thread about VLC player and it's controls.
Adding xaml elements for VLC player to WPF application
After some time, I found a possible solution here:
Integrate VLC player in C# (WPF) project using Vlc.DotNet
I changed it just a little to fir my specific case:
Xaml:
<Window x:Class="WpfApp1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vlc="clr-namespace:Vlc.DotNet.Wpf;assembly=Vlc.DotNet.Wpf"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<vlc:VlcControl x:Name="vlcPlayer" />
<MediaElement x:Name="movOld" HorizontalAlignment="Left" Height="100" Margin="57,74,0,0" VerticalAlignment="Top" Width="100" Source="Resources/Global_Large_crate_opens.mp3"/>
</Grid>
Code:
public MainWindow()
{
InitializeComponent();
vlcPlayer.MediaPlayer.VlcLibDirectory = new DirectoryInfo(#"D:\VideoLAN\VLC\");
vlcPlayer.MediaPlayer.EndInit();
Uri thisUri = new Uri(#"C:\Users\User\Documents\Visual Studio 2017\Projects\WpfApp1\WpfApp1\Resources\VideoFallingFromTable.ogv", UriKind.RelativeOrAbsolute);
vlcPlayer.MediaPlayer.Play(thisUri);
}
This code works perfectly, when I give a full path from my computer.
However, for my game, I need to take the path from MediaElements, like movOld in this case.
However, when I try to do this:
Uri thisUri = new Uri(movOld.Source.ToString(), UriKind.RelativeOrAbsolute);
vlcPlayer.MediaPlayer.Play(thisUri);
I get this exception:
{"This operation is not supported for a relative URI."}
All my attempts to get file name, or file path as a string, or to use "ToAbsolute()" brought same extension.
Can someone please help me find where the error is, and how to fix it?
Since the game is to be used on many computers, with different possible installation places, it is of vital importance to load the new source for existing MediaElements.
I'm not sure why you are using Media Element when you have VLC control. Anyways, make sure that the Media Element source path "Resources/Global_Large_crate_opens.mp3" exists. Are you sure that its a forward slash there? usually in Windows, paths use a backslash. Also if your path is absolute then you need to define it like below:
Uri thisUri = new Uri(movOld.Source.ToString(), UriKind.Absolute);
vlcPlayer.MediaPlayer.Play(thisUri);
The problem is only with your Media Element Source path. Just make sure whatever you put there exists and accessible.
Update:
I recently worked with VLC control, where I built my own User Control to hide VLC library logic and exposed File Path & other dependency properties to specify them from XAML.
You can try using the minimal version of that control in your app.
public partial class VLCControl : UserControl
{
public VLCControl()
{
InitializeComponent();
var currentDirectory = AppDomain.CurrentDomain.BaseDirectory;
string platform = Environment.Is64BitProcess ? "x64" : "x86";
var dirPath = System.IO.Path.Combine(currentDirectory, "vlclib", platform);
vlc.MediaPlayer.VlcLibDirectory = new DirectoryInfo(dirPath);
vlc.MediaPlayer.EndInit();
}
public void Play()
{
if (vlc.IsInitialized && !String.IsNullOrWhiteSpace(FilePath))
{
var options = new string[]
{
String.Format("input-repeat={0}", Repeat)
};
vlc.MediaPlayer.SetMedia(new Uri(FilePath), options);
if (!String.IsNullOrWhiteSpace(AspectRatio))
vlc.MediaPlayer.Video.AspectRatio = AspectRatio;
vlc.MediaPlayer.Play();
}
}
public void Stop()
{
vlc.MediaPlayer.Stop();
}
public string AspectRatio
{
get { return (string)GetValue(AspectRatioProperty); }
set { SetValue(AspectRatioProperty, value); }
}
public static readonly DependencyProperty AspectRatioProperty =
DependencyProperty.Register("AspectRatio", typeof(string), typeof(VLCControl));
public string FilePath
{
get { return (string)GetValue(FilePathProperty); }
set { SetValue(FilePathProperty, value); }
}
public static readonly DependencyProperty FilePathProperty =
DependencyProperty.Register("FilePath", typeof(string), typeof(VLCControl));
public int Repeat
{
get { return (int)GetValue(RepeatProperty); }
set { SetValue(RepeatProperty, value); }
}
public static readonly DependencyProperty RepeatProperty =
DependencyProperty.Register("Repeat", typeof(int), typeof(VLCControl), new PropertyMetadata(-1));
private void vlc_Loaded(object sender, RoutedEventArgs e)
{
Play();
}
}
The XAML file code is:
<UserControl x:Class="VLCControl"
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"
xmlns:vlc="clr-namespace:Vlc.DotNet.Wpf;assembly=Vlc.DotNet.Wpf"
d:DesignHeight="300" d:DesignWidth="300" >
<vlc:VlcControl x:Name="vlc" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Loaded="vlc_Loaded"/>
</UserControl>
If it helped you then don't forget to accept the answer :)
I'm currently building a product configurator in C#/WPF. The Product is something like a custom outlet strip. It's made out of an aluminum profile with variable length. You can choose the outlets and place them along the aluminum profile.
We already have the data model. We have the class "profile" with properties like "length" and the class "items" with properties like "position", "type" etc.
I made a funktion to visualise the data. I have a MainCanvas where im drawing the profile as rectangle and for the witdth I'm using the property "width". The items that belong to that product are in a list. I'm drawing the items with using a for-loop to insert a wpf for each plug in the list "items".
When I'm changing the data, I have to clear the canvas and redraw. I think the next step will be binding the variables of the class to the WPF properties, for example the position of a plug or the lenth of the profile.
Later, the plugs should be select/dragable to change their position (in a grid of 5mm , for example). That's the pint where I'm stuck. I know I can select and drag the plugs with actions like "OnLeftMouseButton". The question now is: In what type of container should I put the plugs in?
I thought I could wrap the WPF-Code of a plug inside a UserControl. Is that the right approach for that? As far as I know, I can make a UserControl selectable with Hittest. The Hittestresult will be put in a list selected. For displaying the selection I could use the Borderthickness/Brush of the UserControl. For dragging, I could change the Position with a ManupilationDelta (and change the binded Position-Variable). Because the count of the plugs is variable, I have to generate the UserControls from C#-Code.
I know that Hittest is not easy to implement working with UserControls, because they are not really "visible".
I'm kind of new to C# and I have a hard time finding someone on the internet with similar problems or projects, maybe because I'm searching for the wrong words. Are my assumptions correct? What WPF controls would you use for that?
I used this answer and add few modifications.
I used an ItemControl to create the view containing your different objects. That way, you can add images just by adding object to a list. The container is a Canvas, but it can be anything since the position is controlled by RenderTransform :
<ItemsControl Name="MainView" ItemsSource="{Binding ListObjects}">
<ItemsControl.ItemsPanel >
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:MVDragableObject}">
<local:DragableObject/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the code behind. Some basic ModelView:
public class MVObjectManager
{
public ObservableCollection<MVDragableObject> ListObjects { get; set; }
public MVObjectManager()
{
ListObjects = new ObservableCollection<MVDragableObject>();
}
}
public class MVDragableObject
{
}
And the code to fill and bind the container. You can notice that I added 3 items to the collection:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MVObjectManager Manager=new MVObjectManager();
Manager.ListObjects.Add(new MVDragableObject());
Manager.ListObjects.Add(new MVDragableObject());
Manager.ListObjects.Add(new MVDragableObject());
MainView.DataContext = Manager;
}
}
I defined a very simple UserControl. It is up to you to customize it:
<UserControl x:Class="StackFill.DragableObject"
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">
<Grid>
<Rectangle Fill="Red" Height="30" Width="30"/>
</Grid>
</UserControl>
And here is the code behind that manage the Drag and Drop behavior:
public partial class DragableObject : UserControl
{
public DragableObject()
{
InitializeComponent();
this.MouseLeftButtonDown += new MouseButtonEventHandler(DragableObject_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(DragableObject_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(DragableObject_MouseMove);
}
protected bool isDragging;
private Point clickPosition;
private void DragableObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isDragging = true;
var draggableControl = sender as UserControl;
clickPosition = e.GetPosition(this.Parent as UIElement);
draggableControl.CaptureMouse();
}
private void DragableObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDragging = false;
var draggable = sender as UserControl;
draggable.ReleaseMouseCapture();
}
private void DragableObject_MouseMove(object sender, MouseEventArgs e)
{
var draggableControl = sender as UserControl;
if (isDragging && draggableControl != null)
{
Point currentPosition = e.GetPosition(this.Parent as UIElement);
var transform = draggableControl.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = new TranslateTransform();
draggableControl.RenderTransform = transform;
}
transform.X = snapPosition(currentPosition.X - clickPosition.X, 10);
transform.Y = snapPosition(currentPosition.Y - clickPosition.Y, 10);
}
}
private double snapPosition(double position, double gridSize)
{
return (Math.Truncate(position / gridSize) * gridSize);
}
}
You can control the snap precision by changing the gridSize argument.
I am trying to print a WPF visual (a Canvas to be exact) containing multiple images. The image sources are loaded from base64 strings that are converted to BitmapSources. When the canvas is shown in a Window the 2 images are displayed correctly, however when its printed using PrintVisual method of PrintDialog, the images both appear the same. I have created a scaled down example that exhibits the behavior that I am seeing.
Here is the XAML I'm using:
<Window x:Class="ImagePrintTest.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ImagePrintTest"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="600">
<StackPanel>
<Button Content="Print" Click="Button_Click" />
<Canvas x:Name="ImageCanvas">
<Image x:Name="ImageA" Canvas.Left="50" Canvas.Top="50" Width="380" Height="56" />
<Image x:Name="ImageB" Canvas.Left="50" Canvas.Top="150" Width="380" Height="56" />
</Canvas>
</StackPanel>
And this is the code behind:
public partial class MainWindow : Window
{
string ABC_DATA = #"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";
string DEF_DATA = #"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";
public MainWindow()
{
InitializeComponent();
ImageA.Source = Convert(ABC_DATA);
ImageB.Source = Convert(DEF_DATA);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog() == true)
{
printDialog.PrintVisual(ImageCanvas, "image print test");
}
}
public static BitmapSource Convert(string s)
{
byte[] bytes;
bytes = System.Convert.FromBase64String(s);
BitmapSource source;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(bytes))
{
source = PngBitmapDecoder.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
}
return source;
}
}
I am assuming that the image sources are being cached somehow somewhere but I really cant explain this behavior. Can anyone explain what is happening here?
It works for me when I create a second BitmapFrame like this:
public static BitmapSource Convert(string s)
{
using (var ms = new MemoryStream(System.Convert.FromBase64String(s)))
{
return BitmapFrame.Create(
BitmapFrame.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.OnLoad));
}
}
However, I don't have an explanation for this behaviour.
I also noticed that printing seems to spoil the layout. After printing, the ImageCanvas moves to position (0,0) in its StackPanel parent.
Imagine next situation: I do have application window with several usercontrols inside. They was displayed side by side in past, but now I want to show one of them in popup window. Not in Popup control but new Window.
See example XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication3="clr-namespace:WpfApplication3"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<wpfApplication3:UserControl1 Visibility="Hidden"
x:Name="UserControl1"/>
<Button Click="ButtonBase_OnClick"
Width="100"
Height="60">open window</Button>
</Grid>
In code behind I need to deattach usercontrol from current Window and assign to new one:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var parent = VisualTreeHelper.GetParent(UserControl1);
if (parent != null)
{
parent.RemoveChild(UserControl1);
}
var w = new Window
{
Content = UserControl1,
Title = "sample",
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = ResizeMode.CanResize
};
w.Show();
}
And after calling w.Show() I always getting blank white window.
If in button click handler change
Content = UserControl1
to
Content = new UserControl1()
I will get right content as well.
But I can't use this way because I want to keep my usercontrol state during pop-out and pop-in events.
So how can I show in new window existing usercontrol without recreating it?
I am not sure how you are calling RemoveChild on a DependencyObject as that method doesn't seem to exist. Note that VisualTreeHelper.GetParent returns a DependencyObject so, the code you posted should not compile unless you have an Extension method somewhere defining RemoveChild.
In your case what you want to do is cast your parent object to type Grid or Panel and then remove the UserControl from the Children property, then set your UserControl as the Content of your window.
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Grid parent = VisualTreeHelper.GetParent(UserControl1) as Grid;
if (parent != null)
{
parent.Children.Remove(UserControl1);
}
var w = new Window
{
Content = UserControl1,
Title = "sample",
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = ResizeMode.CanResize
};
w.Show();
}
There was a similar question asked here
that gives a very detailed answer.
The quick answer is that you will have to remove the control from the main window and then add it to the popup window.