WPF image thumbnail with the offset - wpf

I have an image 800x600 and I would show a thumbnail 90x30 with some offset x=12 and y 12.
I have created a brush but I am struggling to apply an offset.
var source = new ImageBrush(groundSource);
source.Stretch = Stretch.None;
source.AlignmentX = AlignmentX.Left;
source.AlignmentY = AlignmentY.Top;
source.RelativeTransform = new TranslateTransform(0.5, 0);
var grid = new Grid();
grid.ClipToBounds = true;
grid.Background = source;
grid.VerticalAlignment = System.Windows.VerticalAlignment.Top;
grid.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
grid.Margin = new System.Windows.Thickness(12, 12, 0, 0);
grid.Width = SpriteSize.SpriteWidht + 33;
grid.Height = SpriteSize.SpriteHeight;
grid.SnapsToDevicePixels = true;
I would appreciate any suggestions.

There is a hacky solution for this problem:
Add Image as a child to the Grid.
Set Grid attribute as ClipToBounds=true
Set Image margin to control thumbnail offset.

Related

Scaling and moving an item at the same time

I'm trying to create a storyboard that scales and moves an element to a specific position. The basic idea is there are a row of 6 small cards, clicking on one will zoom into a more detailed image, starting from the position and size of the small card and ending up with a large centered card.
The card doesn't end up in the X,Y position I specify, I believe because the scaletransform is also moving it. I saw that a matrixtransform may work, but couldn't get it to work, at least in WP8. Here is my original code, card is the small card for starting location, item is the large detailed card to be scaled and moved
private void ZoomIn(UIElement item, UIElement card)
{
// setup
var _Scale = new ScaleTransform
{
ScaleX = 1,
ScaleY = 1,
CenterX = item.RenderSize.Width / 2,
CenterY = item.RenderSize.Height / 2,
};
var transform = card.TransformToVisual(Application.Current.RootVisual as FrameworkElement);
Point absolutePosition = transform.Transform(new Point(0, 0));
var _Translate = new TranslateTransform();
var _Group = new TransformGroup();
_Group.Children.Add(_Scale);
_Group.Children.Add(_Translate);
item.RenderTransform = _Group;
// animate
var _Storyboard = new Storyboard { };
// scale X
var _ScaleAnimateX = new DoubleAnimation
{
To = 1.0,
From=0.5,
Duration = TimeSpan.FromSeconds(.25)
};
_Storyboard.Children.Add(_ScaleAnimateX);
Storyboard.SetTarget(_ScaleAnimateX, _Scale);
Storyboard.SetTargetProperty(_ScaleAnimateX,
new PropertyPath(ScaleTransform.ScaleXProperty));
// scale Y
var _ScaleAnimateY = new DoubleAnimation
{
To = 1.0,
From = 0.5,
Duration = TimeSpan.FromSeconds(.25)
};
_Storyboard.Children.Add(_ScaleAnimateY);
Storyboard.SetTarget(_ScaleAnimateY, _Scale);
Storyboard.SetTargetProperty(_ScaleAnimateY,
new PropertyPath(ScaleTransform.ScaleYProperty));
// translate (location X)
var _TranslateAnimateX = new DoubleAnimation
{
From = absolutePosition.X,
To = 300,
Duration = TimeSpan.FromSeconds(.25)
};
_Storyboard.Children.Add(_TranslateAnimateX);
Storyboard.SetTarget(_TranslateAnimateX, _Translate);
Storyboard.SetTargetProperty(_TranslateAnimateX,
new PropertyPath(TranslateTransform.XProperty));
// translate (location Y)
var _TranslateAnimateY = new DoubleAnimation
{
From = absolutePosition.Y,
To = 40,
Duration = TimeSpan.FromSeconds(.25)
};
_Storyboard.Begin();
}
I found the solution, the panel with the zoomed card didn't have a horizontal/vertical alignment, I set it to left/top, used CenterX/CenterY of 0, and movements are now pixel perfect

Merge XPS landscape orientation

I've implemented this solution and it worked for me:
Can multiple xps documents be merged to one in WPF?
My problem is that the pages I want to merge are in landscape orientation. When the ContainerVisual is added it creates by default a page in vertical orientation. How can I change the orientation to ContainerVisual?
private void AddXPSDocument(string sourceDocument, SerializerWriterCollator vxpsd)
{
XpsDocument xpsOld = new XpsDocument(sourceDocument, FileAccess.Read);
FixedDocumentSequence seqOld = xpsOld.GetFixedDocumentSequence();
foreach (DocumentReference r in seqOld.References)
{
FixedDocument d = r.GetDocument(false);
foreach (PageContent pc in d.Pages)
{
FixedPage fixedPage = pc.GetPageRoot(false);
double width = fixedPage.Width;
double height = fixedPage.Height;
Size sz = new Size(width, height);
fixedPage.Width = width;
fixedPage.Height = height;
fixedPage.Measure(sz);
fixedPage.Arrange(new Rect(new Point(), sz));
//fixedPage.UpdateLayout();
ContainerVisual newPage = new ContainerVisual();
newPage.Children.Add(fixedPage);
vxpsd.Write(newPage);
}
}
xpsOld.Close();
}
You need to add a RotateTransform to the page visual.
Visual originalPage = Paginator.GetPage(pageNumber).Visual;
var pageContentVisual = new ContainerVisual();
TransformGroup group = new TransformGroup();
group.Children.Add(new RotateTransform { Angle = 90.0 });
pageContentVisual.Transform = group;
pageContentVisual.Children.Add(originalPage);
Note: The above was copied from a custom DocumentPaginator however you should be able to apply it your situation.

Draw a Path programmatically

I'm learning drawing shapes in WPF. I want draw a Path programmatically, not through XAML. I don't know what can assign to Data property.
Path p = new Path();
p.Data = ???
Look at the sample in the MSDN:
//Add the Path Element
myPath = new Path();
myPath.Stroke = System.Windows.Media.Brushes.Black;
myPath.Fill = System.Windows.Media.Brushes.MediumSlateBlue;
myPath.StrokeThickness = 4;
myPath.HorizontalAlignment = HorizontalAlignment.Left;
myPath.VerticalAlignment = VerticalAlignment.Center;
EllipseGeometry myEllipseGeometry = new EllipseGeometry();
myEllipseGeometry.Center = new System.Windows.Point(50,50);
myEllipseGeometry.RadiusX = 25;
myEllipseGeometry.RadiusY = 25;
myPath.Data = myEllipseGeometry;
myGrid.Children.Add(myPath);
It is the line myPath.Data = myEllipseGeometry; that you are looking for. Just assign it a Geometry object.
Here's an example of something I was experimenting with:
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Data;
namespace GridNineExperiment
{
public class Hamburger : Button
{
static GeometryCollection DataHamburger = new GeometryCollection
{
new RectangleGeometry {Rect = new Rect{X = 0, Y = 0, Width = 20, Height = 5 }},
new RectangleGeometry {Rect = new Rect{X = 0, Y = 10, Width = 20, Height = 5 }},
new RectangleGeometry {Rect = new Rect{X = 0, Y = 20, Width = 20, Height = 5 }},
};
static Path PathHamburger = new Path
{
Fill = new SolidColorBrush(Colors.White),
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 1.0,
Data = new GeometryGroup { Children = DataHamburger }
};
public Hamburger()
{
Content = PathHamburger;
}
}

WPF Table Column Sizes

I'm rendering a Table in a WPF FlowDocument using code-behind. But, I've been unable to find an example that shows how to make the table use only the space needed based on content. Instead the table takes up all available width, which I don't want, nor do I want to have to specify a exact pixel sizes.
I'm clearly missing something simple, anyone see it?
var fd = new FlowDocument();
Table t = new Table();
t.BorderBrush = Brushes.Black;
t.BorderThickness = new Thickness(2);
// I thought this would do what I wanted...
t.Columns.Add(new TableColumn() { Width = GridLength.Auto });
t.Columns.Add(new TableCOlumn() { Width = GridLength.Auto });
TableRowGroup trg = new TableRowGroup();
TableRow currentRow = new TableRow();
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("ABC"))));
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("XYZ"))));
trg.Rows.Add(currentRow);
currentRow = new TableRow();
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("123"))));
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("789"))));
trg.Rows.Add(currentRow);
t.RowGroups.Add(trg);
fd.Blocks.Add(t);
I do not think this is possible... the only hacky workaround is to use the BlockUIContainer and a real grid!
var fd = new FlowDocument();
Grid g = new Grid();
g.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
g.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
g.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
g.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
var t1 = new TextBlock() { Text = "ABC", Margin = new Thickness(5,3,5,3) };
t1.SetValue(Grid.ColumnProperty, 0);
t1.SetValue(Grid.RowProperty, 0);
g.Children.Add(t1);
var t2 = new TextBlock() { Text = "XYZ", Margin = new Thickness(5, 3, 5, 3) };
t2.SetValue(Grid.ColumnProperty, 1);
t2.SetValue(Grid.RowProperty, 0);
g.Children.Add(t2);
var t3 = new TextBlock() { Text = "123", Margin = new Thickness(5, 3, 5, 3) };
t3.SetValue(Grid.ColumnProperty, 0);
t3.SetValue(Grid.RowProperty, 1);
g.Children.Add(t3);
var t4 = new TextBlock() { Text = "789", Margin = new Thickness(5, 3, 5, 3) };
t4.SetValue(Grid.ColumnProperty, 1);
t4.SetValue(Grid.RowProperty, 1);
g.Children.Add(t4);
fd.Blocks.Add(new BlockUIContainer(g));
try TableCell.ColoumnSpan and TableCell.RowSpan
I know the question was asked 9 years ago, but I found out an alternate way to do this without using a BlockUIContainer which quite frankly is a pain when serializing the FlowDocument or when the user is just editing the document in a RichTextBox.
Add a PreviewMouseMove and PreviewMouseDown handler to every single table cells. In PreviewMouseMove change the cursor to SizeWE whenever adjacent to the cell border. In PreviewMouseDown capture the mouse using the RichTextBox as the source.
Add a PreviewMouseMove and PreviewMouseUp handler to the RichTextBox. In PreviewMouseMove resize the table column based on the calculated horizontal delta movement. In PreviewMouseUp release the mouse.
The tricky part is to figure where the cell borders are because there's no way out of the box to just get the cell position or width. So you have to approximate where they are by doing the sum of the PagePadding, Table Padding and column widths.

Scaled ellipse over button, button not clickable

I'm scaling an ellipse in an animation with the following code:
ScaleTransform myScTransform = new ScaleTransform();
TransformGroup myTransGroup = new TransformGroup();
myTransGroup.Children.Add(myScTransform);
newPHRadio.RenderTransform = myTransGroup;
newPHRadio.RenderTransformOrigin = new Point(0.5, 0.5);
Storyboard story = new Storyboard();
DoubleAnimation xAnimation = new DoubleAnimation(1, ph.Bereik, new Duration(TimeSpan.FromSeconds(2)));
DoubleAnimation yAnimation = new DoubleAnimation(1, ph.Bereik, new Duration(TimeSpan.FromSeconds(2)));
DoubleAnimation doorzichtig = new DoubleAnimation(1, 0, new Duration(TimeSpan.FromSeconds(2)));
Storyboard.SetTarget(xAnimation, newPHRadio);
Storyboard.SetTarget(yAnimation, newPHRadio);
Storyboard.SetTarget(doorzichtig, newPHRadio);
DependencyProperty[] propertyChainX = new DependencyProperty[] {
Ellipse.RenderTransformProperty,
TransformGroup.ChildrenProperty,
ScaleTransform.ScaleXProperty
};
DependencyProperty[] propertyChainY = new DependencyProperty[] {
Ellipse.RenderTransformProperty,
TransformGroup.ChildrenProperty,
ScaleTransform.ScaleYProperty
};
string thePath = "(0).(1)[0].(2)";
Storyboard.SetTargetProperty(xAnimation, new PropertyPath(thePath, propertyChainX));
Storyboard.SetTargetProperty(yAnimation, new PropertyPath(thePath, propertyChainY));
Storyboard.SetTargetProperty(doorzichtig, new PropertyPath(Ellipse.OpacityProperty));
story.Children.Add(xAnimation);
story.Children.Add(yAnimation);
story.Children.Add(doorzichtig);
story.Duration = new Duration(TimeSpan.FromSeconds(60 / ph.Frequentie));
story.RepeatBehavior = RepeatBehavior.Forever;
story.Begin();
The ellipse is constructed with the following code:
Ellipse newPHRadio = new Ellipse();
newPHRadio.Width = 1;
newPHRadio.Height = 1;
newPHRadio.SetValue(Canvas.LeftProperty, ph.xPositie + 7);
newPHRadio.SetValue(Canvas.TopProperty, ph.yPositie + 7);
newPHRadio.SetValue(Canvas.ZIndexProperty, 3);
newPHRadio.Stroke = new SolidColorBrush(Colors.Black);
newPHRadio.StrokeThickness = 0.03;
Now the ellipse is scaled over an button which has a z-index of 1. With a static ellipse and no fill, the button is clickable. Now there is no fill as well but the button is not clickable. Can someone tell me how to fix this?
With the code you provided the button is clickable.
But if you set the Fill of the ellipse to anything but null, even to Brushes.Transparent, the click will not make it to the button anymore.
Try explicitly setting the Fill of the ellipse to null:
newPHRadio.Fill = null;

Resources