I'm writing an application with WPF Prism using MVVM.
My app utilizes 1280x800px touch screen, that is set as secondary screen and has the taskbar hidden. My main screen is 1920x1080px and is set as primary.
I want my app to start on the secondary screen and cover all visible space on it and here I got strange behavior. I can set the screen to display on like that:
MainWindow.xaml:
<Window x:Class="MyApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
WindowState="{Binding WinState}"
WindowStyle="{Binding WinStyle}"
Left="{Binding PositionX}"
Top="{Binding PositionY}"
Height="{Binding Height}"
Width="{Binding Width}"
Title="{Binding Title}" >
<Grid>
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</Grid>
MainWindowViewModel.cs:
using Prism.Mvvm;
using System.Windows;
using System.Windows.Forms;
using System.Linq;
namespace MyApp.ViewModels
{
public class MainWindowViewModel : BindableBase
{
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public WindowState WinState
{
get { return windowState; }
set { SetProperty(ref windowState, value); }
}
public WindowStyle WinStyle
{
get { return windowStyle; }
set { SetProperty(ref windowStyle, value); }
}
public double PositionX
{
get { return positionX; }
set { SetProperty(ref positionX, value); }
}
public double PositionY
{
get { return positionY; }
set { SetProperty(ref positionY, value); }
}
public int Height
{
get { return height; }
set { SetProperty(ref height, value); }
}
public int Width
{
get { return width; }
set { SetProperty(ref width, value); }
}
#region Fields
private string _title = "";
private WindowState windowState;
private WindowStyle windowStyle;
private double positionX;
private double positionY;
private int height;
private int width;
#endregion
public MainWindowViewModel()
{
InitScreen();
}
private void InitScreen()
{
var screens = Screen.AllScreens.ToList();
var screen = Screen.AllScreens.Where(s => !s.Primary).First();
WinState = WindowState.Normal;
WinStyle = WindowStyle.SingleBorderWindow;
if(screen != null) //Secondary screen detected
{
//Set second screen as app display screen
this.PositionX = screen.WorkingArea.Left;
this.PositionY = screen.WorkingArea.Top;
this.Height = screen.WorkingArea.Height;
this.Width = screen.WorkingArea.Width;
this.WinState = WindowState.Maximized;
this.WinStyle = WindowStyle.None;
}
}
}
}
Tried also code-behind approach and set similar copy paste this method (with modifications to use embedded WPF properties, not my properties bond to them) to MainWindow.xaml.cs, but it gave me the same results.
My initial problem is that I can set app to display on the second screen but when I try to maximize it programmatically but it makes the app display (maximized) on my primary screen (if I do if with maximize button it works as expected).
In the method above I set WindowStyle to normal at first because of this answer, but it also didn't help.
I've used such workaround but still didn't got what I expected.
I've hidden taskbar on the second screen and set my window height and width to the height and width of the second screen but for some reasons it doesn't match the screen properly (I'got bars on left and right of the app). It's quite strange, because if I inspect the secondary screen data I got such info:
{
Bounds = {
X = -1280
Y = 272
Width = 1280
Height = 800
}
WorkingArea = {
X = -1280
Y = 272
Width = 1280
Height = 800
}
Primary = false
DeviceName = "\\\\.\\DISPLAY2"
}
Why is Y in both WorkingArea and Bounds set to 272?
TL;DR:
I can set my app to display on secondary monitor, but whan I maximize it it's displayed on the primary monitor.
When I hidden taskbar on secondary monitor, set WindowStyle to None (Borderless) and Window size to screen size it doesn't cover all of the secondary screen (I got uncovered area on right and left of the window).
Related
I want to simply capture and display a camera picture on my view, updated every second. However the image container, which is bound to my Bitmapsource CurrentFramestays blank during runtime.
This is my code so far (mostly adopted from an answer of another thread with similar topic:
public class CameraViewModel : ViewModelBase
{
public CameraViewModel()
{
StartVideo();
}
private DispatcherTimer Timer { get; set; }
private VideoCapture Capture { get; set; }
private BitmapSource currentFrame;
public BitmapSource CurrentFrame
{
get { return currentFrame; }
set
{
if (currentFrame != value)
{
currentFrame = value;
SetProperty(ref currentFrame, value);
}
}
}
private void StartVideo()
{
//CurrentFrame = new BitmapImage(new Uri("C:\\Users\\Johannes\\Pictures\\Camera Roll\\asdf.bmp")) as BitmapSource;
Capture = new VideoCapture();
Timer = new DispatcherTimer();
//framerate of 10fps
Timer.Interval = TimeSpan.FromMilliseconds(1000);
Timer.Tick += new EventHandler(async (object s, EventArgs a) =>
{
//draw the image obtained from camera
using (Image<Bgr, byte> frame = Capture.QueryFrame().ToImage<Bgr, byte>())
{
if (frame != null)
{
CurrentFrame = ToBitmapSource(frame);
}
}
});
Timer.Start();
}
public static BitmapSource ToBitmapSource(IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ptr, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
return bs;
}
}
/// <summary>
/// Delete a GDI object
/// </summary>
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
}
A few thing for better understanding:
The ViewModelBase class incoorperates and handles the
INotifyPropertyChange events.
Databinding is working! I have tested
it, by assigning a bmp-file to CurrentFrame in the
StartVideo()Method - and the image shows up in the GUI at runtime.
The SetProperty()Method fires every 1000ms as expected.
When I assigned a file to CurrentFrame to test the databinding, I
saw that it seemed to be of type BitmapImage- maybe that's where the
problem lies?? However from the information I could gather,
BitmapSource should work and show in WPF views...
The captured frame from the camera is not empty. I tried to write it
directly to a image file and it shows the correct content as
expected.
Edit:
For completeness here is also the responsible part of the view:
<UserControl.Resources>
<ResourceDictionary>
<local:CameraViewModel x:Key="vm" />
</ResourceDictionary>
</UserControl.Resources>
<Grid DataContext="{StaticResource vm}">
<Image Source="{Binding CurrentFrame}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
Edit2: Link to Github repository to view code
You must not set
currentFrame = value;
before calling
SetProperty(ref currentFrame, value);
because the check
if (Object.Equals(storage, value)) return;
will always be true then.
Implement the property like this:
public BitmapSource CurrentFrame
{
get { return currentFrame; }
set { SetProperty(ref currentFrame, value); }
}
I have a gif in my project that i can see clear in my project. However when I try to change it to a certain position with a absolutelayout the width/height changes accordingly but the "gif" itself does not change size so for example i can only see the top left corner of the gif if i decrease the size by a lot.
My code looks like this:
[assembly: Dependency(typeof(GifView_iOS))]
namespace Project.iOS
{
public class GifView_iOS : IGif
{
public string Get()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
Interface:
public interface IGif
{
string Get();
}
Control:
public class Gif : WebView
{
public string GifSource
{
set
{
string imgSource = value;
var html = new HtmlWebViewSource();
html.Html = String.Format
(#"<html><body style='background: #000000;'><img src='{0}' /></body></html>",
imgSource);
SetValue(SourceProperty, html);
}
}
}
How I use it in xaml:
<controls:Gif GifSource = "myGif.gif" AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.15, 0.15" AbsoluteLayout.LayoutFlags="All" />
So now the gif changes to my absolutelayout values, however, the "gif" itself does not change so in my case i can only see the top left corner of the gif now. How can I make so that the gif also changes it's size with a absolutelayout?
I inheritance from a canvas control and I create my custom canvas class like this:
public class MyCanvas:Canvas
{
//this list will contains all shape
VisualCollection graphicsList;
List<GraphicsBase> cloneGraphicsList;
int c = 0;
double deltaX = 0;
double deltaY = 0;
public MyCanvas()
:base()
{
graphicsList = new VisualCollection(this);
cloneGraphicsList = new List<GraphicsBase>();
}
public VisualCollection GraphicsList
{
get
{
return graphicsList;
}
set
{
graphicsList = value;
}
}
protected override int VisualChildrenCount
{
get
{
return graphicsList.Count;
}
}
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= graphicsList.Count)
{
throw new ArgumentOutOfRangeException("index");
}
return graphicsList[index];
}
public GraphicsBase this[int index]
{
get
{
if (index >= 0 && index < GraphicsList.Count)
{
return (GraphicsBase)GraphicsList[index];
}
return null;
}
}
public int Count
{
get
{
return GraphicsList.Count;
}
}
}
and in XAML use this code:
<Window x:Class="MyNameSpace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CustomCanvas="clr-namespace:MyNameSpace"
xmlns:WPFRuler="clr-namespace:Orbifold.WPFRuler;assembly=Orbifold.WPFRuler"
Title="PrintVarsDesigner" Height="709" Width="964"
Background="LightGray" Grid.IsSharedSizeScope="False" OverridesDefaultStyle="False"
WindowState="Maximized" WindowStartupLocation="CenterScreen">
<CustomCanvas:MyCanvas x:Name="myCanvas" Background="White" VerticalAlignment="Top"
Width="895" Height="1162">
</CustomCanvas:MyCanvas>
</Window>
the controls does't appear after add its from visual screen or C# code by add child to canvas.
Thanks for any advice.
Inheriting WPF controls is problematic to say the least. WPF control's are "lookless". that is to say, the control itself doesn't know how it's going to be presented. When the control is placed in a window WPF looks for the corresponding ControlTemplate to this specific control.
The problem with inherited controls is that they have no such template. If you want to display it, you'll have to write one yourself, and that's not always simple. You can find an example here, but i'd recommend against it. Use UserControls instead.
I have popup custom windows in my DLL used by 3-4 other DLLs. These windows popup in a modal manner.
Should my custom window be a singleton?
Should it be called as follows:
NeXusCustomWindowDlg.Instance.CustomWndDlg.Show("Recipe properties", ctl, 600, 600);
...
NeXusCustomWindowDlg.Instance.Close()
Relevant code:
public void Show(string title, object content, double width, double height)
{
Window newWindow = new Window
{
BorderBrush = Brushes.Blue,
BorderThickness = new Thickness(3),
Title = title,
Content = content,
WindowStyle = System.Windows.WindowStyle.ToolWindow,
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen,
ResizeMode = System.Windows.ResizeMode.NoResize,
Width = width,
Height = height,
VerticalContentAlignment = VerticalAlignment.Stretch
};
//WindowStyle = System.Windows.WindowStyle.None,
OpenedWindow = newWindow;
newWindow.ShowDialog();
newWindow.Content = null;
}
public void Close()
{
if (OpenedWindow != null)
{
OpenedWindow.Close();
}
}
private Window OpenedWindow
{
get
{
lock (_syncObj)
{
return _openedWindow;
}
}
set
{
lock (_syncObj)
{
_openedWindow = value;
}
}
}
Two sample TextBoxes in a standard color scheme and the following constructor yield Box1 with a gray foreground and Box2 with a black foreground, since Box2's foreground color has been explicitly set.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Box2.Foreground = Brushes.Black;
Box1.IsEnabled = false;
Box2.IsEnabled = false;
}
}
I would like to "unset" the foreground color so Box2 "falls back" to the default disabled color and has a gray foreground when IsEnabled is set to false. Is this possible? If so, how is it done?
Setting the Foreground property to null does not have the desired effect. I want to avoid explicitly setting the Foreground color to Gray if possible, since it would not compatible with customized color schemes.
I am not sure if that's what you mean, but try following code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Box2.Foreground = Brushes.Black;
Box1.IsEnabled = false;
Box2.IsEnabled = false;
Box2.ClearValue(TextBox.ForegroundProperty);
}
}
Use the event IsEnabledChanged to set the foreground of the box.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Box2.Foreground = Brushes.Black;
Box1.IsEnabled = false;
Box2.IsEnabled = false;
Box1.IsEnabledChanged += new DependencyPropertyChangedEventHandler(label1_IsEnabledChanged);
}
void label1_IsEnabledChanged( object sender, DependencyPropertyChangedEventArgs e ) {
//Set the foreground you want here!
}
}
But if you don't want to explicit set the color, try to set it to Transparent o.O