I have a custom tab control but I wish to remove the Left, bottom and right big border like the picture bellow, I dont know how these variable work and searching didnt give me anything related to it. Sorry i just start to learn coding.
Here is the code, i tried to play around with it but it gave me weird borders all the time. Can you help me? Or please tell me how does it work. Thank you!
public virtual void DrawBorder(Graphics g, Rectangle borderBounds)
{
if (Parent.SelectedIndex == -1)
{
using (Pen pen = new Pen(BorderColor))
{
g.DrawRectangleFixed(pen, borderBounds);
}
return;
}
var tabBounds = Parent.GetTabBounds(Parent.SelectedTab);
Point[] pt = new Point[8];
if ((Parent.TabLocation & TabLocation.Top) != TabLocation.None)
{
pt[0] = borderBounds.GetBottomLeft().GetOffset(0, -1);
pt[1] = borderBounds.GetBottomRight().GetOffset(-1, -1);
pt[2] = borderBounds.GetTopRight().GetOffset(-1, 0);
pt[3] = tabBounds.GetBottomRight().GetOffset(-1, -1);
pt[4] = tabBounds.GetTopRight().GetOffset(-1, 0);
pt[5] = tabBounds.GetTopLeft();
pt[6] = tabBounds.GetBottomLeft().GetOffset(0, -1);
pt[7] = borderBounds.GetTopLeft();
}
else if ((Parent.TabLocation & TabLocation.Bottom) != TabLocation.None)
{
pt[0] = borderBounds.GetBottomLeft().GetOffset(0, -1);
pt[1] = tabBounds.GetTopLeft();
pt[2] = tabBounds.GetBottomLeft().GetOffset(0, -1);
pt[3] = tabBounds.GetBottomRight().GetOffset(-1, -1);
pt[4] = tabBounds.GetTopRight().GetOffset(-1, 0);
pt[5] = borderBounds.GetBottomRight().GetOffset(-1, -1);
pt[6] = borderBounds.GetTopRight().GetOffset(-1, 0);
pt[7] = borderBounds.GetTopLeft();
}
using (Pen pen = new Pen(BorderColor))
{
g.DrawPolygon(pen, pt);
}
}
I agree with others suggesting having a reproducible example makes helping easier. So please don't mind if I was doing the wrong guesses ...
First, what is the point to leave away the outside borders of your tab control? One of the UI purpose of a tab control is to group some and optically separate controls them from the rest of those in your UI.
Second, if you still like to do that, why are you (probably) creating your own TabControl subclass and take care of custom drawing at all? You can archive this in the XAML code directly by using the TabControl BorderThickness property:
<TabControl BorderThickness="0">
<TabItem Header="Tab1">
<TextBox Text="First Textbox"/>
</TabItem>
<TabItem Header="Tab2">
<TextBox Text="Second Textbox"/>
</TabItem>
</TabControl>
Finally, if you like to style any control rather than adding / changing its functionality, think about using XAML styling instead of creating a custom control.
Related
I now read many threads and tried and so on. I don't get it.
Question
When using MouseCapture any other control isn't motioned. Cannot click something. No Highlightings happens on mouse over. MouseCaption is blocking that. Clicking Twice is necessary. How to avoid that?
Basically
I created a custom autocompletebox which consists of a textbox for free input and a dropdown as textblock containing suggested result-element by the given input. This is pretty like a standard ComboBox.
From Combobox we know, when it is expanded and one clicks somewhere else the dropdown is collapsed.
I want exactly the same behavior like the combobox uses.
Because I am not the first one asking that, I tried several things but didn't get them fully working.
What I still tried and I failed with.
Adding OnLostFocus event to the textbox does not recognize any mouse clicks to non-focusable Elements.
Using Mous.Caption(this) with PreviewMouseLeftButtonDown to receive any mouse clicks on any place on the window.
Yeah that works! I can collapse my dropdown, Un-Capture the mouse again.
But: The mouse caption prevents me from clicking to other UIElement. Checkboxes and RadioBoxes wont be toggled. Just hovering the mouse over a Checkbox or anything else is not highlighting that element any more. Instead I now need to click twice to check a textbox.
I can't figure out, how to solve that.
Also what did not work was that when the mouse-capture event was fired, I cannot figure out where the click has been made.
source as well as e.OriginalSource are equal to my custom control
Getting the Mouse-Position may be an option. But did not find to get the position of my control related to the mouse-position. any properties on the control return NaN.
At first I was not able to recognize any difference between PreviewMouseLeftButtonDown and MouseLeftButtonDown.
I thought the first one, when directly releasing mouse-capture, would fire the mouseclick event to its original destination without the mouse capture any more. It doesn't.
I got it by using the Hittest. Is it that way to do so?
Some Code
XAML of AutoCompleteBox
<UserControl x:Class="MyProject.Wpf.Application.Control.AutoCompleteBoxControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="AutoCompleteBox"
PreviewMouseLeftButtonDown="AutoComplete_MouseLeftButtonDown">
<Grid>
<StackPanel>
<TextBox Name="AutoCompleteTextBox" Text="{Binding ElementName=AutoCompleteBox, Path=TextBoxText, Mode=TwoWay}" Height="{Binding ElementName=AutoCompleteBox, Path=TextBoxHeight}" Width="{Binding ElementName=AutoCompleteBox, Path=TextBoxWidth}" Padding="5, 3, 5, 3" KeyUp="AutoCompleteTextBoxControl_KeyUp" LostKeyboardFocus="AutoCompleteTextBox_OnLostKeyboardFocus"/>
<Popup Name="ResultStackPopup" IsOpen="True" PlacementTarget="{Binding ElementName=AutoCompleteTextBox}" Placement="Custom">
<Border Name="ResultStackBorder" Width="{Binding ElementName=AutoCompleteBox, Path=SuggestionListWidth}" Height="{Binding ElementName=AutoCompleteBox, Path=SuggestionListHeight}" BorderBrush="Black" BorderThickness="1" Visibility="Collapsed" Background="White" Margin="1,0,1,0" HorizontalAlignment="Left">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Name="ResultStack"></StackPanel>
</ScrollViewer>
</Border>
</Popup>
</StackPanel>
</Grid>
</UserControl>
relevant code behind:
//Whenever the dropdown is expanded, the mouse caption is started:
this.CaptureMouse();
mouse-down event:
private void AutoComplete_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (this.IsMouseCaptured)
{
if (!this.IsMouseInBounds())
{
//Release MouseCaption
this.ReleaseMouseCapture();
//Collapse SuggestionList
var border = (this.ResultStack.Parent as ScrollViewer)?.Parent as Border;
if (border != null)
{
border.Visibility = Visibility.Collapsed;
}
}
}
}
private bool IsMouseInBounds()
{
Point point = Mouse.GetPosition(this);
// Perform the hit test against a given portion of the visual object tree.
HitTestResult result = VisualTreeHelper.HitTest(this, point);
if (result != null)
{
return true;
}
return false;
}
Edit
Unfortunately the Popup is (afaik) not a member of the visualTree. So the hittest for the Popup does not work. So I tried to get the position of the Popup to check with the Mouse-position.
The TransformToAncestor method is to be used as everyone is saying that.
But this does not seem to work properly:
The following three calls do return exactly the same points:
Window parentWindow = Window.GetWindow(this);
Point relativePointThis = this.TransformToAncestor(parentWindow).Transform(new Point(0, 0));
Point relativePointPopup = this.ResultStackPopup.TransformToAncestor(parentWindow).Transform(new Point(0, 0));
Point relativePointBorder = this.ResultStackBorder.TransformToAncestor(parentWindow).Transform(new Point(0, 0));
Is this a bug?
I did not give up and made some steps forward.
Answers to:
1)
Either make the Window Focusable or even don't do it that way.
2)
Using the right CaptureMode by Mouse.Capture(this, CaptureMode.SubTree); at least solves that the DropDown keeps being interactive with mouse-over etc. (ofcourse, because it's a child of the capturing-control).
The rest of the Window keeps beeing blocked by mouse-capture. One may just realizes, that this is also normal behavior for a standard ComboBox. So one could live with that.
3)
Getting the inbound-click-check via the mouse position can be handled with the following code. Notice that the mouse-position is given relative to the given element. In this case the AutoCompleteBox is given itself via this. So the upper left corner will be {0.0, 0.0}.
private bool IsMouseInBounds()
{
//Get MousePosition relative to the AutoCompleteBox
Point point = Mouse.GetPosition(this);
//Actual Width and Heigth of AutoCompleteBox
var widthAuto = this.ActualWidth;
var heightAuto = this.ActualHeight;
var upLeftXAuto = 0.0;
var upLeftYAuto = 0.0;
var downRightXAuto = upLeftXAuto + widthAuto;
var downRightYAuto = upLeftYAuto + heightAuto;
//Actual Width and Height of DropDown
var widthDropDown = this.ResultStackBorder.ActualWidth;
var heightDropDown = this.ResultStackBorder.ActualHeight;
double upLeftXDropDown;
double upLeftYDropDown;
//Actual Position of DropDown (may be aligned right or center)
CalculateAlignmentForPopUp(out upLeftXDropDown, out upLeftYDropDown, 0.0, 0.0, widthAuto, heightAuto, widthDropDown);
var downRightXDropDown = upLeftXDropDown + widthDropDown;
var downRightYDropDown = upLeftYDropDown + heightDropDown;
//Calc IsInbound
return (
point.X >= upLeftXAuto && point.X <= downRightXAuto &&
point.Y >= upLeftYAuto && point.Y <= downRightYAuto
) || (
point.X >= upLeftXDropDown && point.X <= downRightXDropDown &&
point.Y >= upLeftYDropDown && point.Y <= downRightYDropDown
);
}
private void CalculateAlignmentForPopUp(out double newX, out double newY, double xAuto, double yAuto, double widthAuto, double heightAuto, double widthPopup)
{
newX = 0.0;
newY = 0.0;
switch (this.ListHorizontalAlignment)
{
case "Right":
newX = xAuto + widthAuto - widthPopup;
newY = yAuto + heightAuto;
break;
case "Center":
newX = xAuto + widthAuto / 2 - widthPopup / 2;
newY = yAuto + heightAuto;
break;
default:
newY = yAuto + heightAuto;
break;
}
}
4)
No answer on that.
But this is now obsolete when using the solution from 3)
Edit)
No answer on that.
But it is working with any way.
I'm working on a game level editor in WPF using C#.
I have a series of image controls for choosing textures, and I want each image to be clickable, with some visible feedback to show which one is selected.
Here's one of the image controls, along with a green highlight border that shows up when it's clicked:
<Image x:Name="tile_image1" Source="as asphalt_test.png" Stretch="Fill" HorizontalAlignment="Right" VerticalAlignment="Top" Width="50" Height="50" MouseDown="texture_click" Margin="0,93,69,0" RenderTransformOrigin="0.16,2.04"/>
<Border x:Name="tile_border" BorderBrush="Lime" BorderThickness="3" HorizontalAlignment="Right" Height="54" Margin="0,91,65,0" VerticalAlignment="Top" Width="54" Visibility="Hidden" />
My question involves the "texture_click" function.
I want to re-use the same function for each image control, which I can easily assign using the MouseDown attribute in XAML. However, what I don't know is how to tell from within the function which control called it, or how to access that control's property's, such as ".Source". I want to be able to grab the file name of the image, as well as move the coordinates of the green border behind the new selection.
Right now, I just have it hard-coded to the first image control. Clicks on the other images will call the function, but the function will only select the first image (not the one that was actually clicked).
// click on tile 1
private void texture_click (object sender, MouseButtonEventArgs e)
{
tile_border.Visibility = Visibility.Visible;
current_tilefile = tile_image1.Source;
string source_string = Convert.ToString (tile_image1.Source);
int last_slash = source_string.LastIndexOf ('/');
current_tile = source_string.Substring (last_slash + 1, 3);
}
I tried using "sender", since I thought that might be the object that called the function, but that returned an error. I also tried calling the function with "texture_click (this)", but that was also no good. These were, admittedly, complete shots in the dark, so I wasn't surprised.
I'm still pretty new to this software, so any insight you guys can give would be great.
You just have to cast the sender parameter to the control type (Image in this case):
private void texture_click (object sender, MouseButtonEventArgs e)
{
//tile_border.Visibility = Visibility.Visible;
var image = sender as Image;
if (image != null)
{
current_tilefile = image.Source;
string source_string = image.Source.ToString();
int last_slash = source_string.LastIndexOf ('/');
current_tile = source_string.Substring (last_slash + 1, 3);
}
}
Of course, this doesn't give you access to the associated border. One thing you can do is to just dump the border into the Image's Tag property:
<Image x:Name="tile_image1" ... Tag="{Binding ElementName=tile_border}" />
<Border x:Name="tile_border" ... />
Then you can retrieve it, again by casting:
private void texture_click (object sender, MouseButtonEventArgs e)
{
var image = sender as Image;
if (image != null)
{
var border = image.Tag as Border;
if (border != null)
{
border.Visibility = Visibility.Visible;
}
// ...
}
}
Note that this (manipulating UI elements from code-behind) is not the ideal way to write a WPF application. Typically you would do something like this by using an existing control (like a ToggleButton), and re-writing its ControlTemplate so that its IsChecked visual state shows a border. But I realize that is a mouthful ...
I have written a custom WPF UserControl. It's a square with a Grid named Base. To that grid I add an ellipse and two labels (volume and location), which are populated with text pulled from the properties of an object which is given as a parameter upon control instantiation.
Here's the XAML:
<UserControl x:Class="EasyHyb.SampleWellControl"
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:DesignHeight="100" d:DesignWidth="100">
<Grid x:Name="Base">
</Grid>
</UserControl>
And the constructor/event functions in the codebehind:
public SampleWellControl(int size, SourceSample sample)
{
InitializeComponent();
this.sample = sample;
this.Width = this.Height = size;
this.selected = SelectionStatus.Unselected;
double spacing = size / 4;
volume = new Label();
location = new Label();
volume.Content = String.Format("{0:0.00}", sample.volume);
location.Content = sample.well.well;
volume.HorizontalAlignment = location.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
volume.FontFamily = location.FontFamily = new System.Windows.Media.FontFamily("Meiryo UI");
volume.FontWeight = location.FontWeight = FontWeights.Bold;
volume.Background = location.Background = Base.Background = this.Background = Brushes.Transparent;
volume.Margin = new Thickness(0, spacing, 0, 0);
location.Margin = new Thickness(0, spacing * 2, 0, 0);
well = new Ellipse();
well.Width = well.Height = this.Width;
well.StrokeThickness = 3;
Base.Children.Add(well);
Base.Children.Add(volume);
Base.Children.Add(location);
this.MouseEnter += SampleWellControl_MouseEnter;
this.MouseLeave += SampleWellControl_MouseLeave;
this.MouseUp += SampleWellControl_MouseUp;
this.Cursor = Cursors.Hand;
UpdateFillAndStroke();
}
void SampleWellControl_MouseLeave(object sender, MouseEventArgs e)
{
RevertWell();
}
void SampleWellControl_MouseEnter(object sender, MouseEventArgs e)
{
HighlightWell();
}
public void HighlightWell()
{
if (this.selected == SelectionStatus.Pooled)
{
return;
}
if (this.selected == SelectionStatus.Unselected)
{
this.well.Stroke = this.strokes[SelectionStatus.Selected];
}
else
{
this.well.Stroke = this.strokes[SelectionStatus.Unselected];
}
}
public void RevertWell()
{
if (this.selected == SelectionStatus.Pooled)
{
return;
}
if (this.selected == SelectionStatus.Unselected)
{
this.well.Stroke = this.strokes[SelectionStatus.Unselected];
}
else
{
this.well.Stroke = this.strokes[SelectionStatus.Selected];
}
}
Basically, when the mouse enters the control, the stroke of the ellipse should change unless the well has undergone an operation to give it a "Pooled" status.
When the mouse enters the control, it responds exactly as I expect: the MouseEnter event handler fires. However, when a user moves the mouse over one of the labels inside the control, the MouseLeave event fires. So even though the label is ostensibly part of the control The pictures below show what I'm talking about. Print Screen removes the cursors, but I put blue dots to indicate where the cursor is:
Responding properly:
Now it seems to think the mouse has left the control:
I've tried adding MouseEnter and MouseLeave event handlers to the labels, but they don't fire. The cursor also changes from a hand to a pointer when the labels are moused over. I've tried adding MouseEnter and MouseLeave event handlers to the control after it's instantiated within another class. I added transparent backgrounds to the Grid, control, and labels, but that didn't make any difference either.
I also checked in my MouseLeave event handler to see if the mouse was over the control, and it seems that the control is not detecting the cursor as being over the control itself:
if(!this.IsMouseOver)
{
RevertWell();
}
//also tried IsMouseDirectlyOver
I would like MouseLeave to fire only when the cursor exits the square bounds of the control. How can I accomplish this while keeping the labels?
Use a combination of
IsHitTestVisible="False"
on all of your objects added to Base:
volume = new Label();
volume.IsHitTestVisible="False";
and then your container which has the events, give a background
<Grid x:Name="Base" Background="White">
(Also I wanted to comment but reputation is stupid)
Well shucks, after a lot of searching around it turns out the problem was indeed contained within another control. I had another UserControl class, EmptyWellControl, which had a Label. The text position within the label was calculated using the Label's Height property, which resulted in a nonsense value that made the label extend vertically well beyond the dimensions of the window. The label didn't have a background, but nevertheless interfered with the controls whose path it crossed. Since the empty and sample wells were all laid out on the same grid, every SampleWellControl was affected by these labels.
I'm trying to create dynamically text box in WPF. It is very essential that I will have the flexibility to determine where the text box will be - in pixel level.
I have found many answers which use stackpanel to create "run-time" text box - but couldn't find how to construct it according to specified location.
the textbox has to be "word wrap" and I'm using a button click event to create the text box
this is the code for now, I really don't know which methods or properties will be helpful.
thanks :)
private void button1_Click(object sender, RoutedEventArgs e)
{
TextBox x = new TextBox();
x.Name = "new_textbox";
x.TextWrapping= TextWrapping.Wrap;
x.VerticalScrollBarVisibility=ScrollBarVisibility.Visible;
x.AcceptsReturn = true;
x.Margin = new Thickness(5, 10, 0, 0);
}
TextBox x = new TextBox();
x.Name = "new_textbox";
x.TextWrapping= TextWrapping.Wrap;
x.VerticalScrollBarVisibility=ScrollBarVisibility.Visible;
x.AcceptsReturn = true;
x.Margin = new Thickness(5, 10, 0, 0);
HouseCanvas.Children.Add(x);
Canvas.SetLeft(x, 20);
Canvas.SetTop(x, 20);
You probably want to place it in a Canvas, if you care about pixel placement of the textbox itself. You'll need to use x.SetValue(Canvas.LeftProperty, pixelX) [and .RightProperty, etc...] to get the position exactly right. Having not done this myself, I'd guess that you need to put the canvas in the right Z-order (on top), and make it transparent. There may also be issues with events, depending on the z-order. Good luck!
-Kev
we're using the MVVM pattern in our application and in a window, we have two TreeViews allowing to drag items from the first and drop it on the second tree. To avoid code behind, we're using behaviours to bind the drag and drop against the ViewModel.
The behaviour is implemented pretty much like this example and working like a charm, with one bug.
The scenario is a tree which is bigger than the window displaying it, therefore it has a vertical scroll bar. When an item is selected and the user wants to scroll, the program starts drag and drop (which prevents the actual scrolling and therefore isn't what we want).
This isn't very surprising as the scrollbar is contained in the TreeView control. But I'm unable to determine safely if the mouse is over the scrollbar or not.
The TreeViewItems are represented by a theme using Borders, Panels and so on, so a simple InputHitTest isn't as simple as one may think.
Has anybody already encountered the same problem?
If more code coverage of the problem is required, I can paste some lines from the .xaml.
Edit
Incorporating Nikolays link I solved the problem using a IsMouseOverScrollbar method, if anyone has this problem in the future the code from above must be altered in the following way:
private static void PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed || startPoint == null)
return;
if (!HasMouseMovedFarEnough(e))
return;
if (IsMouseOverScrollbar(sender, e.GetPosition(sender as IInputElement)))
{
startPoint = null;
return;
}
var dependencyObject = (FrameworkElement)sender;
var dataContext = dependencyObject.GetValue(FrameworkElement.DataContextProperty);
var dragSource = GetDragSource(dependencyObject);
if (dragSource.GetDragEffects(dataContext) == DragDropEffects.None)
return;
DragDrop.DoDragDrop(
dependencyObject, dragSource.GetData(dataContext), dragSource.GetDragEffects(dataContext));
}
private static bool IsMouseOverScrollbar(object sender, Point mousePosition)
{
if (sender is Visual)
{
HitTestResult hit = VisualTreeHelper.HitTest(sender as Visual, mousePosition);
if (hit == null) return false;
DependencyObject dObj = hit.VisualHit;
while(dObj != null)
{
if (dObj is ScrollBar) return true;
if ((dObj is Visual) || (dObj is Visual3D)) dObj = VisualTreeHelper.GetParent(dObj);
else dObj = LogicalTreeHelper.GetParent(dObj);
}
}
return false;
}
Take a look at this implementation of Drag and Drop behaviour for ListView by Josh Smith. It has code to deal with scrollbars and some other unobvious problems of DnD (like drag treshold, precise mouse coordinates and such). This behaviour can be easily adopted to work with TreeViews too.
I had the same Problem. I solved it by placing the TreeView inside a ScrollViewer.
<ScrollViewer Grid.Column="0">
<TreeView BorderThickness="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseMove="DeviceTree_OnMouseMove" PreviewMouseLeftButtonDown="DeviceTree_OnPreviewMouseLeftButtonDown" Name="DeviceTree" ItemsSource="{Binding Devices}"/>
</ScrollViewer>