Should I use Geometry.Freeze here? - wpf

When a WPF item is frozen, the docs says it cannot be changed. I'm just not very sure what "change" mean by in this context.
For example, if I create an instance of a shape and then freeze it, is it possible to do things like rotate or translate it even though it is frozen?

Short answer:
Once an object is frozen, you cannot modify any properties on it. This applies recursively.
Longer answer:
First of all, the Shape class (and thus Path, Ellipse, Rectangle etc.) are not freezable.
But assuming you are talking about Geometry, which is freezable, then the general answer is no, because attempting to modify properties of a frozen object is not possible. For example, the following code will throw an exception
var geo = new LineGeometry();
geo.Freeze();
// InvalidOperationException:
geo.Transform = new TranslateTransform(10, 10);
And freezing is recursive, so its not possible to cheat the system like this:
var tx = new TranslateTransform(10, 10);
var geo = new LineGeometry();
geo.Transform = tx;
geo.Freeze();
// InvalidOperationException:
tx.X = 20;
But, back to your original question about shapes, which are constructed out of geometries (but doesn't derive from them).
You can freeze the geometry of your shape, and still apply transformations to that shape. This works because the transform is on the shape object, and not on the freezable:
var geo = new LineGeometry(new Point(0,0), new Point(10,10));
geo.Freeze();
var myShape = new Path { Data = geo };
// This is fine, even though the geometry is frozen
myShape.RenderTransform = new TranslateTransform(10, 10);

Related

Is there a way to populate an object instance using AutoFixture?

Here is a sample of what I'm trying to do
var foo = CreateFoo(); // I don't have access to the internals of CreateFoo
fixture.Populate(foo); // foo should now have random property values
Obviously, there is no Fixture.Populate method. But how should I solve this problem?
Thanks!
AutoFixture indeed doesn't provide API to fill writable properties of an existing object. However, you can use the following snippet to achieve the desired behavior:
var fixture = new Fixture();
var objectToFill = new TypeWithProperties();
new AutoPropertiesCommand().Execute(objectToFill, new SpecimenContext(fixture));
Assert.NotNull(objectToFill.Value);
If you need that often, you can create an extension method for the IFixture in your test project.

SurfaceDragDrop never seems to end, object seems stuck in eternal (non)manipulation

I'm developing a WPF application for the Surface, and have run into a bug which neither I nor my colleagues know the answer to.
I have a situation when the user can drag things from a box. The box is just an image, and when touching it, another image is spawned and is the object that gets the interaction, i.e. gets dragged and dropped. The whole dragging is performed with SurfaceDragDrop.BeginDragDrop(...). Upon drop, the object simply disappears.
Now the problem is that occasionally (seems to be when input comes rapidly, just hammering the screen and not actually dragging the objects) the spawned item never despawns, i.e. it remains on screen even when input has been removed. It is possible to intereact with it by touching it to continue dragging it, but it's movement and rotation is off because it appears to think there already is another finger interacting with it.
However, through debugging I have certified that there are no touches registered to the object. It's AreAnyTouchesCaptured property is false. Manipulation is however active. So what seems to happen is that it goes into some sort of manipulation which never ends.
In order to get rid of it, I've implemented a temporary solution which simply does SurfaceDragDrop.CancelDragDrop(..) on such objects that have been on screen a certain period of time without any touches captured.
I hope my description of the problem is somewhat clear, if not please ask. Below is the code used to spawn the objects. I have however not written it (an ex-colleague did), I'm just stuck with the debugging:
private void item_PreviewTouchDown(object sender, TouchEventArgs e)
{
var box = sender as Canvas;
var imgpath = String.Format(#"[someimgpath].png");
var bmp = new BitmapImage(new Uri(imgpath, UriKind.RelativeOrAbsolute));
Image draggedItem = new Image { Source = bmp, Stretch = Stretch.None, Width = bmp.Width, Height = bmp.Height };
draggedItem.UpdateLayout();
var touchPoint = e.TouchDevice.GetTouchPoint(box);
var itemX = touchPoint.Position.X - draggedItem.Width / 2.0;
var itemY = touchPoint.Position.Y - draggedItem.Height / 2.0;
box.Children.Add(draggedItem);
draggedItem.SetValue(Canvas.LeftProperty, itemX);
draggedItem.SetValue(Canvas.TopProperty, itemY);
draggedItem.Visibility = System.Windows.Visibility.Hidden;
//We should perfom drag-and-drop when the size of the draggedItem is updated, since
//BeginDragDrop uses ActualWidth and ActualHeight property
draggedItem.SizeChanged += (o, ev) =>
{
List<InputDevice> devices = new List<InputDevice>();
devices.Add(e.TouchDevice);
foreach (TouchDevice touch in box.TouchesCapturedWithin)
{
if (touch != e.TouchDevice)
{
devices.Add(touch);
}
}
var img = new Image { Source = draggedItem.Source, Stretch = Stretch.None, Width = bmp.Width, Height = bmp.Height };
img.SetValue(Canvas.LeftProperty, itemX);
img.SetValue(Canvas.TopProperty, itemY);
var cursor = SurfaceDragDrop.BeginDragDrop(box, draggedItem, img, draggedItem, devices, DragDropEffects.Move);
};
e.Handled = true;
}

Is there a simple way to set a large number of WPF Form objects?

This is just basic example:
if (a == b)
{
textBox1.Text = "a == b";
textBox1.IsEnabled = true;
textBox2.Text = "EXAMPLE2";
textBox2.IsEnabled = false;
textBox3.Text = "EXAMPLE3";
textBox3.IsEnabled = false;
textBox4.Text = "EXAMPLE4";
textBox4.IsEnabled = false;
textBox5.Text = "EXAMPLE5";
textBox5.IsEnabled = false;
}
else
{
textBox1.Text = "a != b";
textBox1.IsEnabled = false;
textBox2.Text = "EXAMPLE2";
textBox2.IsEnabled = true;
textBox3.Text = "EXAMPLE3";
textBox3.IsEnabled = true;
textBox4.Text = "EXAMPLE4";
textBox4.IsEnabled = true;
textBox5.Text = "EXAMPLE5";
textBox5.IsEnabled = true;
}
This seems pretty tedious. I was wondering if there's a better way to handle situations like that? I just have this feeling that all the Pros out there don't do things using this 1x1x1x.... method.
There are lots of ways to refactor this sort of code to give something more succinct. It really does become a matter of experience and preference to choose the method that is appropriate for each case.
To that end, I've listed below several options that I find serve me well:
Binding
Binding particularly within WPF gives a very powerful way of easily updating form objects. In your Xaml you apply a datacontext (generally for the form/window) and bound property for each object which then allows you to then update properties in your c# code and have them automatically reflected in the form objects.
One big advantage of this is getting all that tedious object.Text = "xyz" code out of the way.
In your given example, you could introduce a boolean property TextBoxesEnabled which is bound to the enabled property of your text boxes, and reflects whether a == b.
Domain objects
This follows on from the binding approach—in your example above it looks like all your properties are dependant of only two external pieces of data ("a" and "b"). In this situation your are often limited to doing something just like you have, where you need to set your UI properties within lots of procedural code.
However, more often you can introduce expressive domain objects that have properties that map directly to UI properties. For example, perhaps you have a User object, with a Name property, and then a user window, with a name textbox.
Now your domain object will be provided by calls into your service layer and data access infrastructure, and due to binding automatically displayed in your UI.
Code refactoring
Even where you are needing to set lots of properties directly without the ability to use more expressive objects and binding, there will often be some simple refactorings that can help.
In the code example you gave, two easy refactorings leap out to me:
a == b is a Boolean evaluation so you could have something like:
bool enableTextBoxes = a == b;
TextBox1.Enabled = enableTextBoxes;
TextBox2.Enabled = enableTextBoxes;
// etc...
You are setting the text to the same thing in the branches of the if, so take that out of the if and just have it once (though I'm guessing this is just because you were giving trivial example code).
You can loop over collections of controls, so if you need to set large numbers of control values to the same thing you can just do iterate over them and assign to each.
Assuming:
1. your controls have a pattern myControl1, myControl2, etc. (e.g textBox1, textBox2, etc)
2. your controls are all children of same FrameworkElement parent (e.g. all of them are within a Grid)
you can try something like:
bool shouldEnable = (a == b); //or whatever logic you want to have to decide the enable/disable value
for(i=0; i<10; i++)
{
string myControlName = "textBox" + i.ToString(); //assuming your pattern to be textBox1, textBox2, etc.
object myControl = myGrid.FindName(myControlName); //assuming your textboxes present in a Grid named "myGrid"
if(myControl is TextBox) //assuming your controls to be TextBoxes
{
TextBox myTextBox = (TextBox)myControl;
myTextBox.IsEnabled = shouldEnable;
myTextBox.Text = "whatever you want based on your logic";
}
}
Do remember:
1. to refer to FindName on MSDN: FrameworkElement.FindName
2. in case all of your controls are not under one parent, you might want to have a look at: How can I find WPF controls by name or type?
3. to be aware of performance implications (you may want to check it).
Also see: Find Controls by Name in WPF

Silverlight InlineCollection.Add(InlineUIContainer) missing?

I'm having difficulty adding the inline of specific type InlineUIContainer into the InlineCollection (Content property) of a TextBlock. It appears the .Add() method of InlineCollection doesn't accept this type, however you can clearly set it through XAML without explicitly marking the content as a InlineContainer, as demonstrated in many examples:
http://msdn.microsoft.com/en-us/library/system.windows.documents.inlineuicontainer.aspx
Is it possible to programatically add one of these as in the following?
Target.Inlines.Add(new Run() { Text = "Test" });
Target.Inlines.Add(new InlineUIContainer() {
Child = new Image() { Source = new BitmapImage(new Uri("http://example.com/someimage.jpg")) } });
Target.Inlines.Add(new Run() { Text = "TestEnd" });
I have a feeling what's going on is that Silverlight is using a value converter to create the runs when specified in XAML as in the example which doesn't use InlineContainer, but I'm not sure where to look to find out.
The specific error I'm getting is as follows:
Cannot add value of type 'System.Windows.Documents.InlineUIContainer' to a 'InlineCollection' in a 'System.Windows.Controls.TextBlock'.
As pointed out by Jedidja, we need to use RichTextBox to do this in Silverlight.
You can't Add() Runs directly, but you can add Spans containing Runs.
Interestingly, you can also do this:
textBlock.Inlines.Clear();
textBlock.Inlines.Add(new Span());
textBlock.Inlines[0] = new Run();
Not that it's a good idea to hack around what the framework is actively trying to prevent you from doing.
P.S. If you can't figure out what XAML is doing, inspect the visual tree.

TDD with DataGridView

I'm relatively new to TDD, and still trying to learn to apply some of the concepts. Here's my situation.
I've got a WinForm with a DataGridView. I'm trying to write a test for the routine to be called by a button click that will perform some operations on the selected rows of the grid.
So I will be passing in the DataGridViewSelectedRowCollection object (i.e, the dgv.SelectedRows property at the time the button is clicked).
The DataGridViewSelectedRowCollection object has no constructor, so the only way I can figure to create it is to put together a DataGridView in my test project, then select some rows and pass in the SelectedRows property. But clearly, I don't want to re-create the whole form there.
So I do a DataGridView dgv = new DataGridView(), and gin up a BindingList (actually a SortableBindingList) just like the grid is bound to in the real application. The test list has 3 rows in it. And I do a dgv.DataSource = myList.
Now, at that point in the real application, the grid view is bound. If I look at dgv.Rows.Count, it's equal to the number of rows in the list. However, in my test, setting the DataSource property to the list still results in zero rows in the grid.
I'm thinking there's something missing in the creation of the gridview that normally gets done when it's added to the control list of the form. It probably initializes the handler for the OnDataSourceChanged event or something, and that isn't being done in my test code, but I'm really at a loss as to how to fix it, again, without re-creating a whole form object in my test fixture.
Here's the relavant code form my test method:
DataGridView residueGrid = new DataGridView();
List<Employee> baseListToGrid = new List<Employee>();
SortableBindingList<Employee> listToGrid = new SortableBindingList<Employee>(baseListToGrid);
residueGrid.DataSource = listToGrid;
for (int ix = 1; ix < 4; ix++)
{
listToGrid.Add(ObjectMother.GetEmployee(ix));
}
Assert.AreEqual(3, listToGrid.Count, "SortableBindingList does not have correct count");
Assert.AreEqual(3, residueGrid.Rows.Count, "DataGrid is not bound to list");
Thanks for any help you can give me.
DataGridView residueGrid = new DataGridView();
List<Employee> baseListToGrid = new List<Employee>();
SortableBindingList<Employee> listToGrid = new SortableBindingList<Employee>(baseListToGrid);
// residueGrid.DataSource = listToGrid; <-- move this line...
for (int ix = 1; ix < 4; ix++)
{
listToGrid.Add(ObjectMother.GetEmployee(ix));
}
// residueGrid.DataSource = listToGrid; <-- ...to here!
Assert.AreEqual(3, listToGrid.Count, "SortableBindingList does not have correct count");
Assert.AreEqual(3, residueGrid.Rows.Count, "DataGrid is not bound to list");
A useful structure for writing test is the following:
public void MyTest()
{
// Arrange
// Act
// Assert
}
In this case, Arrange would be instantiating all the objects, and filling the list. Act is where you set the data source of the gridview, and Assert is where you check that everything went OK. I usually write out those three comment lines each time I start writing a test.
Well, I solved the problem, and pretty much confirmed that it is something being done in the initialization of the control when added to the form that makes the DataSource binding work.
It suddenly dawned on me that that the "target" created by the MS testing framework is a private accessor to the Form itself. So I changed the line
DataGridView residueGrid = new DataGridView();
in the above code to, instead of creating a new DGV object, just reference the one on the target form:
DataGridView residueGrid = target.residueGrid;
That change made everything work as expected.

Resources