I am using the open source library AvalonDock to support drag and drop of multiple tabs (panes) outside and back to the MainWindow and I want to disable most of the possible drop targets (or lets say layouts) like placing a tab below another or placing tabs side by side. In other words I only want to allow placing tabs in a "row of tabs" like in firefox or chrome browser.
Is there any property to disable drop targets (layouts) and if yes, can you please provide me with a short code example?
Here is a simple example of an MainWindow with three dockable panes (LayoutDocuments), which look like the TabItems of the standard TabControl of WPF (sorry, I could not post a screenshot of this):
<Window x:Class="TabTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
Height="300" Width="300">
<Grid>
<xcad:DockingManager VerticalAlignment="Stretch">
<xcad:LayoutRoot>
<xcad:LayoutPanel>
<xcad:LayoutDocumentPane>
<xcad:LayoutDocument Title="Doc1">
</xcad:LayoutDocument>
<xcad:LayoutDocument Title="Doc2">
</xcad:LayoutDocument>
<xcad:LayoutDocument Title="Doc3">
</xcad:LayoutDocument>
</xcad:LayoutDocumentPane>
</xcad:LayoutPanel>
</xcad:LayoutRoot>
</xcad:DockingManager>
</Grid>
</Window>
Thanks for your help!
This answer is written for AvalonDock 2.0. I don't know if this works on other versions of AvalonDock.
In the source code, there is a file Controls/OverlayWindow.cs. Change the code inside the else inside the case DropAreaType.DocumentPane: default: to hide the desired targets no matter what:
void IOverlayWindow.DragEnter(IDropArea area)
{
...
switch (area.Type)
{
...
case DropAreaType.DocumentPane:
default:
{
...
else
{
areaElement = _gridDocumentPaneDropTargets;
_documentPaneDropTargetLeft.Visibility = Visibility.Hidden;
_documentPaneDropTargetRight.Visibility = Visibility.Hidden;
_documentPaneDropTargetTop.Visibility = Visibility.Hidden;
_documentPaneDropTargetBottom.Visibility = Visibility.Hidden;
/* ... */
}
}
break;
}
...
}
The ellipses are to summaries code segments.
Most UI elements in WPF have a property named AllowDrop. If you set this to false, it should stop a dragged element from being dropped on that control. However, there are also methods that you can handle during the drag and drop procedure that give the developer full control over when to disable a drop operation. Perhaps you should take a good read of the Drag and Drop Overview page on MSDN to find out more.
Related
I created a custom TabItem with a DockPanel and a Button in it.
XAML:
<TabItem
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" x:Class="MovieDB.UI.UserControlls.SearchTab" d:DesignWidth="500.038" d:DesignHeight="309.055">
<DockPanel Background="#FFE5E5E5">
<Button x:Name="button" Content="Button" Height="100" Width="75" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</DockPanel>
</TabItem>
C#:
namespace MovieDB.UI.UserControlls
{
/// <summary>
/// Interaktionslogik für SearchTab.xaml
/// </summary>
public partial class SearchTab : TabItem
{
private SearchContainer<SearchMovie> results;
public SearchTab()
{
InitializeComponent();
this.Header = "Suche";
}
public SearchTab(SearchContainer<SearchMovie> results):this()
{
this.updateSearch(results);
}
public void updateSearch(SearchContainer<SearchMovie> results)
{
clear();
if(results.TotalResults == 0)
{
}
else
{
this.results = results;
Debug.WriteLine("Results: " + results.Results.Count());
}
}
private void clear()
{
}
}
}
If launch my program the button is displayed (Screenshot 2). But the button and i guess the panel itselve does noch show up in the Visual Studio 2015 Designer (Screenshot 1).
Where is the Problem?
If you put the TabItem in a TabControl it works fine.I think that's the Problem.
The designer needs a navigation control like TabControl,Window Page etc. as root.
<TabControl x:Class="CustomControls.tab"
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:DesignWidth="500.038" d:DesignHeight="309.055">
<TabItem>
<DockPanel Background="#FFE5E5E5">
<Button x:Name="button" Content="Button" Height="100" Width="75" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</DockPanel>
</TabItem>
</TabControl>
What you did should work. But it doesn't, and I'll later try to show why.
But first, my workaround:
open designer, and right-click on it,
select: Edit Additional Templates, then Edit Generated Content, then Create Empty... Give your new DataTemplate a name, say 'DataTemplate1'
Designer will generate some code - an empty DataTemplate with a key DataTemplate1, and it will add your to TabItem the ContentTemplate attribute, with DataTemplate1 assigned to it (by DynamicResource).
Now move the content of your TabItem to the DataTemplate1. The designer should correctly display your layout.
If you close and reopen your SearchTab control, you'll find that you can't see the content again. To have it back right click on the designer, select Edit Additional Template, then Edit Generated Content, then Edit Current.
Why the designer couldn't load your SearchTab? If you right-click on the designer and select Edit Template, then Edit a Copy... you'll have TabItem style generated. The header of the TabItem is displayed by the ContentPresenter, while the TabItem's content must by displayed inside the innerBorder Border. This border has Opacity set to zero, and it's being changed to 1 by MultiTriggers, which are dependent on the TabControl. The TabControl in your case doesn't exist, so all the bindings are broken. Which suggests, that changing a default Style for 'TabItem', would also solve your problem.
Although I do not have my dev machine to work with, I can only offer a suggestion. For whatever you want on your tab item control, why not put all your controls in a custom user control or grid with the premise that you KNOW you will be adding it to tab control that is dynamically added to your tab page.
The tab item needs its proper parent tab control to render it in the designer. But if you build everything as a standard control make it look and operate as you expect.
Then at run-time, add your tab item, then add the custom control to your dynamically added tab item and see if that works for you. You apparently have all the other components / processes working. Even if your sample control was just a label, textbox or something similar.
Ok, so I am not that versed in the mighty WPF, but I attempted an interesting project to jump into it. I have made a simple RSS/ATOM feed viewer that pulls the HTML out of and RRS or ATOM feed and sticks it in a Browser control which is added to a stack panel... which is the content of a ScrollViewer. Whew. Anyways the problem is, I am doing this all in the code behind and have found that the ScrollViewer doesn't work, or isn't recognizing the size of the content, so there is no scrolling. I have tried setting the size of the viewer and the content, as well as attempted the min and max sizes.
What am I missing here? The content is there, and if I load this before the WPF is loaded it works but once I try to change, or "Clear" children from a control, the scrollviewer stops working right.
<Window x:Class="Heine.Syndication.xkcd.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Feed Viewer" Height="600" Width="800">
<StackPanel>
<ToolBarPanel >
<ToolBar>
<ComboBox Margin="0" Width="100" Name="cbController">
<MenuItem Header="xkcd" Name="xkcdMI"/>
<MenuItem Header="9Gag" Name="nineGagMI"/>
<MenuItem Header="reddit" Name="redditMI"/>
</ComboBox>
</ToolBar>
</ToolBarPanel>
<Grid Name="svMain">
</Grid>
</StackPanel>
</Window>
public MainWindow()
{
InitializeComponent();
cbController.SelectedIndex = 0;
xkcdMI.Click += xkcdMI_Click;
nineGagMI.Click += nineGagMI_Click;
redditMI.Click += redditMI_Click;
Load("http://xkcd.com/atom.xml");
}
private void Load(string feedUrl)
{
var reader = XmlReader.Create(feedUrl);
var feed = SyndicationFeed.Load<SyndicationFeed>(reader);
svMain.Children.Clear();
var tmpStack = new StackPanel();
foreach (var item in feed.Items)
{
var browser = new WebBrowser();
GetHTML(ref browser, item);
tmpStack.Children.Add(browser);
}
svMain.Children.Add(new ScrollViewer()
{
Content = tmpStack,
Height = svMain.Height
});
}
Okay, so I am unfortunately answering my own question, without going crazy and rewriting a bunch of stuff. So in my research, it turns out that in .NET 4.0 and 4.5, StackPanel is great with ScrollView... so long as you know what you are doing! I agree with the comments left that MVVM is what is happening in the background, and my code actually reflects what I had to change it to to try and get it working, even when I had proper models, views, listeners/handlers etc (which are all built into the framework).
So the answer to my question, given the above, and this link I found that setting the size of my Grid, which contained the scrollview and other such fun made it work as advertised. The problem is evidently that the Grid was reporting to the scrollview that it was indefinably big, and so the scrollviewer could be too. So... for my code above, I need to handle when the whole form is resized and set the height of my grid accordingly.
<Grid Name="svMain" Height="550">
</Grid>
How can I get ScrollViewer to work inside a StackPanel?
We are using the prism and WPF to build application. Recently we started using UI Automation (UIA) to test our app. But some strange behavior occurred when we run UIA test. Here's simplified shell:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0" Grid.Column="0"
Name="loadingProgressText"
VerticalAlignment="Center" HorizontalAlignment="Center"
Text="Loading, please wait..."/>
<Border
Grid.Row="0"
x:Name="MainViewArea">
<Grid>
...
</Grid>
</Border>
<!-- Popup -->
<ContentControl
x:Name="PopupContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="PopupRegion"
Focusable="False">
</ContentControl>
<!-- ErrorPopup -->
<ContentControl
x:Name="ErrorContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="ErrorRegion"
Focusable="False">
</ContentControl>
</Grid>
In our app, we use layers (Popup and ErrorPopup) to hide MainViewArea, to deny access to the controls. To show Popup, we use next method:
//In constructor of current ViewModel we store _popupRegion instance to the local variable:
_popupRegion = _regionManager.Regions["PopupRegion"];
//---
private readonly Stack<UserControl> _popups = new Stack<UserControl>();
public void ShowPopup(UserControl popup)
{
_popups.Push(popup);
_popupRegion.Add(PopupView);
_popupRegion.Activate(PopupView);
}
public UserControl PopupView
{
get
{
if (_popups.Any())
return _popups.Peek();
return null;
}
}
Similar to this, we show ErrorPopup over all elements of our application:
// In constructor we store _errorRegion:
_errorRegion = _regionManager.Regions["ErrorRegion"]
// ---
private UserControl _error_popup;
public void ShowError(UserControl popup)
{
if (_error_popup == null)
{
_error_popup = popup;
_errorRegion.Add(_error_popup);
_errorRegion.Activate(_error_popup);
}
}
Mistics...
When we run it as users do it (double click on app icon), we can see both custom controls (using AutomationElement.FindFirst method, or through Visual UI Automation Verify). But when we start it using UI Automation test - ErrorPopup disapears from the tree of the controls. We trying to start the application like this:
System.Diagnostics.Process.Start(pathToExeFile);
I think that we missed something. But what?
Edit #1
As #chrismead said, we tried to run our app with UseShellExecute flag set to true, but this does not help. But if we start app from cmd line, and manually click the button, Popup and ErrorPopup are visible in automation controls tree.
Thread appThread = new Thread(delegate()
{
_userAppProcess = new Process();
_userAppProcess.StartInfo.FileName = pathToExeFile;
_userAppProcess.StartInfo.WorkingDirectory = System.IO.Directory.GetCurrentDirectory();
_userAppProcess.StartInfo.UseShellExecute = true;
_userAppProcess.Start();
});
appThread.SetApartmentState(ApartmentState.STA);
appThread.Start();
One of our suggestion is when we use method FindAll or FindFirst to search the button to click, window somehow cached its UI Automation state, and does not update it.
Edit #2
We have find, that extension method of prism library IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) have some strange behavior. If we stopped use it, this solve our problem particulary. Now we able to see ErrorView and any kind of view in PopupContentControl, and application updates UIA elements tree structure. But this is not an answer - "Just stop use this feature"!
In MainViewArea we have a ContentControl, which updates it content depending on user actions, and we are able to see only the first loaded UserControl to that ContentControl.Content property. This is performed like this:
IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.MainContentRegion, this.Uri);
And if we change the view, no updates will performed in UI Automation tree - the first loaded view will be in it instead. But visually we observe another View, and WPFInspector shows it properly (its show not a UI Automation tree), but Inspect.exe - not.
Also our suggestion that window use some kind of caching is wrong - caching in UI Automation client we have to turn on explicitly, but we don't do it.
I'm sorry that I've missed some detail, that was the key to the answer. I think that it was not important thing. Anyway.
We used NavBar from DevExpress controls library for WPF. What turns out, is when NavBar is present, dynamically created views are not appears on the UI Automation tree. When remove it from the window, there was an ability to see all dynamically loaded views. What does the NavBar - still mistic for me.
Here bright example to see what happened, if NavBar is present or absent on the Window (DevExpress is required).
MainWindow.xaml:
<Window xmlns:dxn="http://schemas.devexpress.com/winfx/2008/xaml/navbar"
x:Class="Test.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 Name="ContentGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<!--Comment NavBar to see dynamic control in UI Automation tree-->
<dxn:NavBarControl Name="asdasd">
<dxn:NavBarControl.Groups>
<dxn:NavBarGroup Header="asdasdasdasd" />
</dxn:NavBarControl.Groups>
</dxn:NavBarControl>
<TextBox Grid.Column="1" Name="Statictb" Text="static is visible in ui automation tree" />
<Button Grid.Row="1" Content="Create controls" Height="25" Click="Button_Click"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
TextBox tb = new TextBox();
Grid.SetRow(tb, 1);
Grid.SetColumn(tb, 1);
tb.Text = "dynamic is not visible, if NavBar here...";
ContentGrid.Children.Add(tb);
}
}
Edit
According to the DevExpress answer on their support site:
After a peer is created, listening of automation events may cause performance issues. We have decided to clear invocation lists of automation events to resolve it. In your specific situation, you need to disabling clearing. To do it, please set the static DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled property to False in the Window constructor.
This solve the problem.
My guess is that the ContentControl's automation peer should update its children with AutomationPeer.ResetChildrenCache() after the view has been changed.
AutomationPeer.InvalidatePeer() should have the same effect (in addition to other side effects) and it is supposed to be called automatically in response to the LayoutUpdated event. You might want to check that the LayoutUpdated event is raised when the view changes.
stukselbax, try to find a sequence of keystrokes (TABs, and an ENTER most likely) to click the button that enables you to see the items. it is pretty easy to send keystrokes and i can add more in here about that if that works for you. you can always establish a tab order in your application that makes the most sense for users.
------ Update on 6/20/12 --------
Have you tried double clicking a shortcut to your app on the desktop using PInvoke to see if you can see the controls when it is opened that way? Here is a link to an example here on stackoverflow:
Directing mouse events [DllImport("user32.dll")] click, double click
Another idea: some of the controls on the app I am currently automating don't show up in the tree until a mouse click occurs on them. To accomplish this without using any hardcoded coordinates, I find something in the tree which is just (above/below/etc) the place where I need to click to get the control to appear. I then get the mouse coordinates for that item and put the mouse at a small offset from there and click. Then I can find my controls in the tree. If the app is resized, moved around, etc. this will still work since the small offset is still valid.
I am trying to migrate an existing Winforms project into WPF. However: there are some user controls I need to leave as WinForm controls.
I have added a WinForms UserControl into a WPF Window. It consists of a RichTextBox and some buttons and labels. This is subclassed into various further user controls.
When I embed the UserControl into a WPF window it renders - but none of the buttons appear to do anything. When underlying processes update e.g. the RichTextBox it does not display the content. Yet when I inspect the textbox in debug I can see the content (though I have to click on 'base' to see this.)
[ One difference I have spotted - though it may not be relevant - is that when this control is on a WPF and non-working Visual Studio shows the object as 'sealed' but when in the original Winforms project when it is fully working it does not show as sealed. ]
I have added code to change the text in the labels - and they also firmly refuse to update: yet again I can see the text if I examine the label in debug mode.
This stack overflow question may address the same issue:
WindowsFormsHost Winform pdfviewer control problem
but the answer didn't make a lot of sense to me:
It mentioned replacing
new Window { Content = CreateContent(), Title = title }.Show();
But this is not a piece of code I recognise: I am using a xaml file with code behind and it's called up using
System.Windows.Application app = new System.Windows.Application();
app.Run(new FormWPFApp());
(where FormWPFApp is my name for the WPF window)
Here is the xaml header:-
<Window x:Class="ZedApp.FormWPFApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Printers="clr-namespace:ZedApp.UserControls.Printers"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="Conversion version" Height="661" Width="1559" Loaded="Window_Loaded">
Here is the xaml I use for the two UserControls (they both inherit from the same base class) :-
<WindowsFormsHost Height="430" HorizontalAlignment="Left" Margin="192,32,0,0" Name="windowsFormsHostTicketPrinter" VerticalAlignment="Top" Width="324" Grid.Row="1" Grid.Column="1">
<Printers:TicketPrinter x:Name="ticketPrinter">
</Printers:TicketPrinter>
</WindowsFormsHost>
<WindowsFormsHost Height="430" HorizontalAlignment="Left" Margin="522,32,0,0" Name="windowsFormsHostJournalPrinter" VerticalAlignment="Top" Width="324" Grid.Row="1" Grid.Column="1">
<Printers:JournalPrinter x:Name="journalPrinter">
</Printers:JournalPrinter>
</WindowsFormsHost>
[Another thing I have noticed is a method that clears the Rich Text Box on one of the windows starts kicking out errors of the following type if run under WindowsFormsHost in WPF -
"Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
private void ClearRichTextBox(RichTextBox rtbToClear)
{
if (rtbToClear.IsHandleCreated)
{
if (rtbToClear.InvokeRequired)
{
this.Invoke(new Action<RichTextBox>(ClearRichTextBox), new object[] {rtbToClear});
return;
}
rtbToClear.Clear();
}
}
]
What is the likely cause of this behaviour and what do I need to do to get the elements within the User Control working?
Proper input interop with WinForms requires some cooperation between the host and the WPF input system. The topic Message Loops Between Win32 and WPF in the SDK explains this well. In your setup, the easiest way to make this happen is to use code like this:
Window w = new Window1();
System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop(w);
w.Show();
ElementHost.EnableModelessKeyboardInterop() essentially registers an input hook with the WinForms Application object (which normally runs the message loop) and calls ComponentDispatcher.RaiseThreadMessage().
I found something about this issue for ASP, but it didn't help me much ...
What I'd like to do is the following: I want to create a user control that has a collection as property and buttons to navigate through this collection. I want to be able to bind this user control to a collection and display different controls on it (containing data from that collection).
Like what you had in MS Access on the lower edge of a form ...
to be more precise:
When I actually use the control in my application (after I created it), I want to be able to add multiple controls to it (textboxes, labels etc) between the <myControly> and </mycontrol>
If I do that now, the controls on my user control disappear.
Here is an example of one way to do what you want:
First, the code - UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent", typeof(object), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
public object MyContent
{
get { return GetValue(MyContentProperty); }
set { SetValue(MyContentProperty, value); }
}
}
And the user control's XAML - UserControl1.xaml
<UserControl x:Class="InCtrl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" Name="MyCtrl">
<StackPanel>
<Button Content="Up"/>
<ContentPresenter Content="{Binding ElementName=MyCtrl, Path=MyContent}"/>
<Button Content="Down"/>
</StackPanel>
</UserControl>
And finally, the xaml to use our wonderful new control:
<Window x:Class="InCtrl.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:InCtrl"
Title="Window1" Height="300" Width="300">
<Grid>
<me:UserControl1>
<me:UserControl1.MyContent>
<Button Content="Middle"/>
</me:UserControl1.MyContent>
</me:UserControl1>
</Grid>
</Window>
I'm having a hard time understanding your question, but I think what you're describing is an ItemsControl using DataTemplates to display the contents of (presumably) an ObservableCollection(T).
A UserControl may not be the best way to do this. You're wanting to add decorations around content, which is basically what Border does: it has a child element, and it adds its own stuff around the edges.
Look into the Decorator class, which Border descends from. If you make your own Border descendant, you should be easily able to do what you want. However, I believe this would require writing code, not XAML.
You might still want to make a UserControl to wrap the buttons at the bottom, just so you can use the visual designer for part of the process. But Decorator would be a good way to glue the pieces together and allow for user-definable content.
Here's a link to a built-in control (HeaderedContentControl) that does the same thing as the accepted answer except that it is an existing control in WPF since .Net 3.0