I'm writing a simple "tutorial" library that will allow developers to easily add step-by-step tutorials to their existing WPF applications. The tutorials will help first time users of the application find their way around by adding an overlay that highlights a control and explains its purpose. The end result will look something like this:
The regular application:
The overlay explaining the purpose of a control:
My question is this: What's the most reliable and unobtrusive way to inject the overlay view into the current window? The best I've come up with so far is to require the developer to add an attached property to whatever window will be hosting the overlay, and then add the necessary elements on the window's Initialized callback:
public static void IsTutorialOverlayCompatibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((Boolean)e.NewValue == true)
{
if (sender as Window != null)
{
Window window = (Window)sender;
window.Loaded += new RoutedEventHandler((o, eargs) =>
{
Grid newRootElement = new Grid();
newRootElement.Name = "HelpOverlayRoot";
if (window.Content as UIElement != null)
{
UIElement currentContent = (UIElement)window.Content;
window.Content = null;
newRootElement.Children.Add(currentContent);
newRootElement.Children.Add(new HelpOverlayControl());
window.Content = newRootElement;
}
});
}
}
}
This feels like a hack, however, and I'm not sure that there isn't some edge case where this method will break the layout of the application. In addition, it requires that the window's Content property be an instance of type UIElement.
I'd like to avoid forcing developers to change their XAML (i.e, adding a custom overlay UserControl to every window) in order to use my library. What's the best way to add this kind of functionality to an existing WPF application?
Related
I have a custom control inherited from Frame. (In a nutshell, it's kind of a custom alert box which I show over the whole app content).
I am using Custom Renderer to achieve this.
In xaml the control is located directly on the page (among other controls) (actually, I am creating it in the condebehind, but that makes no difference).
(Implementing it for iOS so far only).
The showing/hiding is initiated by IsVisible XF property. Then, I am adding the container view (native one) into the root of the app.
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsVisible")
{
// showing/hiding here
I am having two issues in this situation:
1. Right on this event raising the content positioning of the native view generated is not quite initialized: the iOS Frame values of the views don't have any widths/heights setup. That all probably done right after, so what I do is the following:
Task.Run(async () =>
{
await Task.Delay(10);
InvokeOnMainThread(() =>
{
SetupLayout();
Subviews[0].Alpha = 1;
SetupAnimationIn();
});
});
... which generally works, but still not quite slightly, and the approach is neither reliable nor nice.
On IsVisible = false it's even worse: I cannot handle the leaving animation as the element content got destroyed by XF engine (I suppose) right after (or even before) the notification raised, so the element disappears instantly, which doesn't look nice for the user experience.
So, is there any nice way to handle those things?
It's probably a little late, but I thought I would offer some guidance for anyone else trying to do something similar. For (1) I'm not quite sure what you mean that the native view is not quite initialized, but here is something you might find useful:
var measurement = Measure(screenWidth, screenHeight);
var contentHeight = measurement.Request.Height;
You can read more about what the 'Measure' method does in the docs, but basically it gives you the minimum dimensions of the view element based on its content. The arguments are constraints, so use the maximum size that the view might be. You can use it to initialize the dimensions manually by setting 'WidthRequest' and 'HeightRequest'.
(2) Basically you need to override the IsVisible property like this:
public static new readonly BindableProperty IsVisibleProperty = BindableProperty.Create(
nameof(IsVisible), typeof(bool), typeof(MyCustomView), default(bool), BindingMode.OneWay);
public new bool IsVisible
{
get => (bool)GetValue(IsVisibleProperty);
set => SetValue(IsVisibleProperty, value);
}
Note the use of the new keyword to override the inherited property. Of course, then you will need to handle the visibility yourself.
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == IsVisibleProperty.PropertyName)
{
if (IsVisible) AnimateIn();
else AnimateOut();
}
}
Now you can handle the visibility however you want. Hopefully this is of some help!
To preface this question, I am working on coding the back end of an application whose UI was put together by someone else (I believe using Blend). The application consists of a series of "Screens," whose root element in XAML is "UserControl". There is no use of the "Window" tag anywhere in the source.
What I want to do is remove the Windows border that is added to the outside edge of the application when I run the program. The border currently consists of forward/backward buttons like a web browser, and an X button to close.
All I can find from searches are instructions to add
WindowStyle="None"
to the
<Window>
element. But of course, I don't have one of those, and WindowStyle is not a property of UserControl. Anyone know how to accomplish this with UserControl root elements?
Edit: The StartupUri for the application is
this.StartupUri = new Uri(#"pack://application:,,,/WpfPrototype1.Screens;Component/Screen_1.xaml");
the file it points to does not have a Window tag.
Based on the comments above it seems your MainWindow is created dynamically somewhere, however you can use the Application class to get the applications MainWindow.
var mainWindow = Application.Current.MainWindow;
And you can then set your border style from there
Example:
private void RemoveBorder()
{
var mainWindow = Application.Current.MainWindow;
if (mainWindow != null)//should never be
{
mainWindow.WindowStyle = System.Windows.WindowStyle.None; // removes top bar (icon, title, close buttons etc)
mainWindow.AllowsTransparency = true; //removes the border around the outside
}
}
I have a multi-window application in WPF, where my main application window is invisible (Visibility=Collapsed) containing visible child windows. Application creates child windows on-demand. I need an algorithm to determine the coordinates and dimensions of the newly created child window. Obviously, the new child window should not cover (fully) another child window. Does WPF offer any support whatsoever to implement this kind of logic ? Or, do I have to do everything on my own. I imagine this would be a lot of work. The behavior I am looking for is very similar to Sticky Notes behavior in Windows 7.
Part of my code will help you to really understand what I mean:
public void ViewModelsCollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (ViewModel viewModel in e.NewItems)
{
View view = new View(viewModel);
view.Owner = SleekNoteUI.App.Current.MainWindow;
...
}
}
}
I am writing WinForm application in C# .NET and I need to add dashed/dotted or any other type of border to any of UI components of application when the user clicks on it. I would like to get something like WinForm GUI editor in Visual Studio.
I am new in .NET so I don't know well what is possible via native methods and properties and what I need to implement myself. I have tried to find something on the net and here but I am not sure what to search, there are different approaches. For example it is possible to draw the border artificially, I mean using graphics. But I guess there should be easier approach.
What can you advice? What is the best practice in this situation? Please provide some portions of code.
Every Control has a Paint event. You have to subscribe to this event and look into the given arguments. The sender is the current control that should be painted. You can cast it within your method to Control. Now you can check the control if it focused by checking control.Focused and if it is true simply do whatever you like within the graphics object of the PaintEventArgs. This can furthermore be encapsulated in an extension method which would make the usage fairly easy.
public static void DrawBorderOnFocused(this Control control)
{
if(control == null) throw new ArgumentNullException("control");
control.Paint += OnControlPaint;
}
public static void OnControlPaint(object sender, PaintEventArgs e)
{
var control = (Control)sender;
if(control.Focused)
{
var graphics = e.Graphics;
var bounds = e.Graphics.ClipBounds;
// ToDo: Draw the desired shape above the current control
graphics.DrawLine(Pens.BurlyWood, new PointF(bounds.Left, bounds.Top), new PointF(bounds.Bottom, bounds.Right));
}
}
The usage within the code would then be something like:
public MyClass()
{
InitializeComponent();
textBox1.DrawBorderOnFocused();
textBox2.DrawBorderOnFocused();
}
OK, first for context look at the Windows desktop; You can take items (folders, files) on the desktop and drag them around to different places and they "stay" where you dragged them. This seems to be a pretty useful feature to offer users so as to allow them to create their own "groupings" of items.
My question is thus:
Is there a control in .NET that approximates this behavior with a collection of items?
I'm thinking something like a listview in "LargeIcon" mode, but it allows you to drag the icons around to different places inside the control.
You can do this with a standard ListView control by implementing drag-and-drop. Here's a sample control that does this:
using System;
using System.Drawing;
using System.Windows.Forms;
public class MyListView : ListView {
private Point mItemStartPos;
private Point mMouseStartPos;
public MyListView() {
this.AllowDrop = true;
this.View = View.LargeIcon;
this.AutoArrange = false;
this.DoubleBuffered = true;
}
protected override void OnDragEnter(DragEventArgs e) {
if (e.Data.GetData(typeof(ListViewItem)) != null) e.Effect = DragDropEffects.Move;
}
protected override void OnItemDrag(ItemDragEventArgs e) {
// Start dragging
ListViewItem item = e.Item as ListViewItem;
mItemStartPos = item.Position;
mMouseStartPos = Control.MousePosition;
this.DoDragDrop(item, DragDropEffects.Move);
}
protected override void OnDragOver(DragEventArgs e) {
// Move icon
ListViewItem item = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
if (item != null) {
Point mousePos = Control.MousePosition;
item.Position = new Point(mItemStartPos.X + mousePos.X - mMouseStartPos.X,
mItemStartPos.Y + mousePos.Y - mMouseStartPos.Y);
}
}
}
I think the closest would the ListView control, but even that is more like an explorer window. You might be able to create your own view that does what you want, but you'd need to manually persist icon locations somewhere.
If you are not opposed to using WPF, Josh Smith has created a pretty neat canvas that I am currently using for a project. It allows you to add controls and drag them around the canvas. You would have to handle what is loaded on the canvas and where on the next load of the program, but that is pretty simple.
http://www.codeproject.com/KB/WPF/DraggingElementsInCanvas.aspx
This depends on whether this is a windows application or a web browser based application. In either case you need to have some sort of container to manage the locations of controls. You can manage the position of controls inside of a container with their X and Y coordinates.
You would handle the actual movement using the drag events. So you have drag start, while dragging (you might show a place holder graphic or change the cursor), and finally a drag end (set the control's x and y to the new position). Obviously these aren't the actual event names, but a search for "how to handle drag events" should get you started.
In a web environment, I know jquery has dragging capability built in. So you might want to look at that. The one big thing you'll have to be careful of is maintaining the positions of your controls between postbacks. I'm not sure what would happen in this case.
Windows uses ListView32, an internal control with drag n' drop placeholder features, custom borders...
The icon location can be stored in a XML file, or in the application settings (by putting the XML as string and converting it to file when needed).
You can do, for example:
<icons>
<icon1>
<name>Icon1</name>
<text>My PC</text>
<imageIndex>16</imageIndex>
</icon1>
<icon2>
.....
</icon2>
.....
</icons>
Lorenzo