I have a DataGrid which users can drag and drop files into for them to be imported into the program. I wanted to add some user feedback for when this is happening, so I have put an overlay over the DataGrid when the user drags and drops. The current look of the overlay is this:
I don't like the way the overlay goes over the top of the DataGrid header. If possible, I'd like to confine the overlay to the content only. Does anyone know of some way I could do this? I know I could hard code the height of the header or perhaps create a custom control but the more lightweight/unobtrusive the solution the better in my opinion. Here's a diagram of what I want in case it wasn't clear:
More information: In my current implementation I simply have both the DataGrid and the overlay inside a Grid, and turn the opacity of the overlay on and off as needed. The skinny rectangle under my DataGrid is a loading bar. I'd prefer the overlay not to go over that either, although it doesn't in the current implementation so I don't think that's an issue.
Edit: Here's how I ended up solving it, it's pretty quick and dirty but it works.
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
var header = WPFHelper.FindVisualChild<DataGridColumnHeadersPresenter>(FilesDataGrid);
// Include outside border in margin
var border = FilesDataGrid.BorderThickness;
Thickness margin = new Thickness(border.Left, border.Top, border.Right, border.Bottom);
margin.Top += header.ActualHeight;
OverlayGrid.Margin = margin;
}
There is nothing more "lightweight/unobtrusive" than setting the top-margin of the overlay element to the actual height of the header. So if you don't want to overcomplicate things, you could just set the Margin to a hardcoded value of 23.96:
<Grid>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="..." Binding="{Binding}" />
</DataGrid.Columns>
</DataGrid>
<Border Background="Silver" Opacity="0.5" Margin="0 23.96 0 0" />
</Grid>
The more flexible solution would be to write a behaviour that finds the DataGridColumnHeadersPresenter in the visual tree and set the top-margin of the overlay element to its ActualHeight.
Related
I'm creating a simple WPF app in which one of the core features would be that wherever the user clicks on the grid inside the main window, a number of buttons should appear around the position of the click.
Now, I try to achieve this with only 1 button. I know that I have to capture the current position of the mouse and then modify the 4 arguments of the Margin of the button (left, top, right, bottom) by creating new instances of Thickness-es.
I managed to create new Thickness-es to the Margins, with the left and top argument set to the mouse X and Y cordinates respectively, but I don't know how to calculate or what to use as the right, and bottom arguments of the newly created Margins.
Here is the relevant function from the xaml.cs (the values in question are indicated as 0-s and grid is intented to refer to the grid):
private void Grid_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var mouseLocation = PointToScreen(Mouse.GetPosition(grid));
RandomButton.Margin = new Thickness(mouseLocation.X, mouseLocation.Y, 0, 0);
}
Here is the relevant part of the xaml:
<StackPanel>
<Button
Name="RandomButton"
Height="30"
Width="30"
Background="#FF130889"
Click="RandomButton_Click"
Content="RandomContent" />
</StackPanel>
It is also worth mentioning that when the button's HorizontalAlignment is set to Left and the VerticalAlignment is set to top, the button seem to do what I want with this setup, but only, when the windowsize is full.
I think I have to use the actual height of the window or the grid, but I don't know how. I know it is something simple, but I just started working with WPF, so I apreciate any kind of help!
As far as I understood the problem is in determining the click relative position.
In this case you can use Mouse.GetPosition method.
Here is the example with Canvas:
private void SetPos()
{
var relativePosition = Mouse.GetPosition(this.MainCanvas);
Canvas.SetLeft(this.btn1, relativePosition.X);
Canvas.SetTop(this.btn1, relativePosition.Y);
btn1.Visibility = Visibility.Visible;
}
I have a WrapPanel and buttons are programmatically created and added as children of the WrapPanel. So, I want to show vertical scrollbar when the WrapPanel is full of buttons (children) to be able to add more buttons continuously.
If we need a scrollbar shown, do we have to bring ScrollViewer? Isn't there a way without ScrollViewer? What I want to get is, because the WrapPanel is of small size, I want a scrollbar to be shown only when needed (like full of children).
My code is simple as below (WrapPanel inside Grid and the Grid is inside TabControl)
Many thanks always for your excellence.
Update: I struggled in finding solution on internet for even several days. And I tried put WrapPanel inside ScrollViewer. However, though I set the VerticalScroll to auto, the vertical scrollbar is always shown even when the WrapPanel doesn't have any children.
Furthermore, when I intentionally make the WrapPanel full of children (buttons), the vertical scrollbar of ScrollViewer doesn't provide scrolldown availability.
And the buttons at the bottom line of WrapPanel shown cut and more, I can't scroll down to see beyond the button at the bottom line. I made buttons to be placed beyond the bottom line of WrapPanel intentionally.
With or without, I want the vertical scrollbar to be shown when only needed (full of children). It seems very easy to be done. But it's difficult to make it work properly.
Solution: was provided by Mr. Henk Holterman
<DropShadowEffect/>
</Button.Effect>
</Button>
<WrapPanel x:Name="WrapPanelGreen" HorizontalAlignment="Left" Height="180" VerticalAlignment="Top" Width="232" UseLayoutRounding="True" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
</Grid>
</TabItem>
</TabControl>
And below is my simple code which make button programmatically and add as a child of WrapPanel.
for (int k = 0; k < Overviews.Length; k++)
{
Button btnoverviewcontent = new Button();
ToolTip tt = new ToolTip();
tt.Content = "Press this button if you want to modify or delete.";
btnoverviewcontent.ToolTip = tt;
btnoverviewcontent.Cursor = Cursors.Hand;
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(255, 101, 173, 241);
btnoverviewcontent.Background = mySolidColorBrush;
btnoverviewcontent.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
btnoverviewcontent.Padding = new Thickness(3, 3, 3, 3);
btnoverviewcontent.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch;
TextBlock textBlock = new TextBlock()
{
Text = Overviews[k],
TextAlignment = TextAlignment.Left,
TextWrapping = TextWrapping.Wrap,
};
btnoverviewcontent.Content = textBlock;
btnoverviewcontent.BorderThickness = new Thickness(0, 0, 0, 0);
btnoverviewcontent.FontStretch = FontStretches.UltraExpanded;
btnoverviewcontent.Margin = new Thickness(5, 5, 5, 5);
WrapPanelGreen.Children.Add(btnoverviewcontent);
btnoverviewcontent.Click += new RoutedEventHandler(OnOverviewClick);
The idea in WPF is that every component has only its own job and if you want certain behavior, you combine multiple components to create the view you are looking for.
This means that in order to get a scroll bar for a panel, you will have to wrap it in a ScrollViewer component. That’s the purpose of the ScrollViewer and that’s the only (sane) solution to solve this.
However, though I set the verticalscroll to auto, the verticalscrollbar is always shown even when the Wrappanel doesn't have any child […]
Then you seem to be using the ScrollViewer incorrectly, or wrapping the wrong element. It should look like this:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<WrapPanel>
<!-- Any number of components here -->
</WrapPanel>
</ScrollViewer>
If I place lots of example labels inside that, then I do get a scroll bar as soon as the window is not large enough to show them all. But if there is enough room, the scroll bar is not displayed.
Note that the ScrollViewer itself needs to have the proper dimensions in the parent element, so make sure that it’s not larger than the visible area. It is also necessary for the WrapPanel (or whatever other element you wrap with the ScrollViewer) to have auto widths and heights. Otherwise, with fixed dimensions, the dimensions of the panel will not change as you modify the panel’s content and as such the scrolling status will not change.
See this complete example with a dynamic number of elements:
<Window x:Class="WpfExampleApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="200">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<WrapPanel Name="panel">
<Button Click="Button_Click">Add child</Button>
</WrapPanel>
</ScrollViewer>
</Window>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Label element = new Label() { Content = "This is some example content" };
panel.Children.Add(element);
}
}
...just in case someone is looking at this post after 4.5 years and feels, that the solution doesn't help at all:
I had a similar problem, which was caused by a StackPanel into which my ScrollViewer and dynamic buttons where put in...
Seems like the StackPanel expands the height limitless, so that the ScrollViewer and the WrapPanel always expand their height past the window-bounds. so you couldn't scroll at all. Wrapping that StackPanel into a ScrollViewer helped in my case (although eventually i had to change the controls in the end...)
regards
hello I have the following problem: I want to draw a rectangle on the canvas with methods Canvas.SetLeft() and Canvas.SetTop().
I use the method UserControl_Loaded() and everything works.
the problem is that having ActualWidth when resizing the window and therefore the grid, the value does not change and I left with the values no longer accurate.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Rectangle rett = new Rectangle();
rett.Height = grid1.ActualHeight-10;
rett.Width = grid1.ActualWidth -10;
rett.Fill = new SolidColorBrush(Colors.LightBlue);
canv.Children.Add(rett);
Canvas.SetLeft(rett, 10);
Canvas.SetTop(rett, 10);
}
this is the xaml:
<Grid x:Name="grid1">
<Canvas x:Name="canv" Height="auto" Width="auto"></Canvas>
</Grid>
in the first picture it is fine when not resize the window.
the second when I resize the grid remains the previous width.
I want the width of the rectangle was updated when changing the width of the grid.
Thank you.
Without a good, minimal, complete code example that clearly illustrates your question, along with a detailed explanation of what you're actually trying to accomplish (especially in a broader sense), it is impossible to know for sure what the best answer in your case would be.
Taking your question literally, it seems one possible approach would be to bind the Rectangle dimensions to the Grid's dimensions, so that they are updated as the Grid changes size. You can use IValueConverter to subtract the appropriate amount from the actual dimensions.
But that's a fairly complicated solution for what would otherwise be a reasonably simple problem, and especially so given that you seem to be doing this in code-behind for some reason (not ideal in the first place, and setting up bindings in code-behind is particularly tedious).
Idiomatically, what you should probably be doing is not putting the Rectangle in the Canvas at all, but rather making it a child of the Grid directly. Then you can set its alignments to Stretch so that it will fill the grid cell it's in. Finally, you can set its margins so that you have the 10 pixel gap on the top and left, and no gap on the right and bottom.
For example:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Rectangle rett = new Rectangle();
rett.Fill = new SolidColorBrush(Colors.LightBlue);
// NOTE: technically don't need to set these, as Stretch is the default value!
rett.HorizontalAlignment = HorizontalAlignment.Stretch;
rett.VerticalAlignment = VerticalAlignment .Stretch;
// 10 pixels of margin on top and left, none on right and bottom
rett.Margin = new Thickness(10, 10, 0, 0);
grid1.Children.Add(rett);
}
Doing it as above allows the XAML layout engine to automatically handle the resizing behavior you are looking for.
All that said, I would definitely encourage you to implement this in XAML instead of code-behind. There are a lot of things code-behind is good at, but frankly XAML is much better at any of the things directly related to the configuration of your GUI object graph.
I have images (on local drive) that are of 200x275 in dimension and I'm filling one row in my data grid with them using the following code:
XAML : DataGrid.Columns
<DataGridTemplateColumn Header="IMG" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Width="200" Height="275" Margin="0,0,0,-100" Source="{Binding Path=IMG}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
In my xaml.cs file I have IMG as a property of type BitmapImage (stores the uri as BitmapImage) and using a CollectionViewSource to update it with changes to the list of which includes a column for those images.
Displaying them is fine, but I only want to display a section of each image as a sort of preview (same width of 200px, 10px from the top, and only 50px in height - later I'll have the full image display when the small preview of it is clicked).
From the above bit of code you can see that I tried changing the Margin values (-100 to bottom) - which kinda works, but the last image in the list is displayed in full and extends past the bottom of the list.
Is there a better way of cropping 10px from the top, and then set the height to 50px without the image scaling down?
The only way I can think of doing it is creating a new bitmap from the bitmap image but I'm pretty sure that'd be really inefficient with hundreds of images.
I'd try wrapping your Image control in a Grid with ClipToBounds=true. That should fix it. Just make sure the grid is sized to the item (50px high) and not auto-sized from the image or the clipping won't work as you expect.
Some of you maybe find this question dull but I am still not deeply accustomed to wpf drawing. I want to add formatted text on a Rectangle which moves around on a canvas and I have got a hint to override the UIElement.OnRender method. However I do not know if I should override the canvas class or the Shape class. In any correct case, to what refers the drawingContext parameter of the method as described in the example: http://msdn.microsoft.com/en-us/library/bb613560.aspx#FormattedText_Object ?
Is the text ultimately assigned to the shape or is it a visual temporary effect that cannot move along with the shape on the canvas?
Is there any further effective means of drawing text on a shape?
You can draw Text on top of a Rectangle by placing both controls in a parent container that allows controls to overlap, such as a Grid or a Canvas
<Grid>
<Rectangle Fill="Red" Stroke="Black"
HorizontalAlignement="Stretch" VerticalAlignment="Stretch" />
<Label Content="Test"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
You can then apply whatever formatting you want to the Label, the Rectangle, and you can move the group around by setting the positioning of the Grid
Rachel's answer is correct, although you can extend it a bit, have some UserControl defined as:
And in the codebehind define 1. Label:String DependencyProperty, Shape:UIElement DependencyProperty.
Handle the Shape's change event and call:
private void UpdateShape()
{
grdShapeContainer.Children.Clear();
if(this.Shape != null)
{
grdShapeContainer.Children.Add(this.Shape);
}
}
This way you will be able to make things dynamic.
Regards,
Artak
You might also want to look into ZIndex property which can be set on objects like Grid (<Rectangle Background="Black" Grid.ZIndex = 99 /> for instance would put it overtop other items) which useful for making things like "loading" screens.