I have a collection view whose layout is managed by a UICollectionViewFlowLayout instance. I have a long press gesture recognizer attached to the collection view whose job is to detect if a long press occured within the bounds of one of the cells in the collection view and if so, delete that cell after changing the cell's transform animatedly.
This is the relevant part of the action performed by the gesture recognizer:
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
[UIView animateWithDuration:2.0 animations:^{
cell.layer.transform = CATransform3DMakeScale(1.0, 0.0, 1.0); // an example animation - squish the cell vertically
} completion:^(BOOL fin) {
//[cell.layer removeAllAnimations];
[self.model.numb3rs removeObjectAtIndex:[indexPath row]]; // update model
[self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]]; // delete item
}];
The problem is that the deletion method seems to perform its own property change/animation on the cell which doesn't gel well with my animation. For instance, in the effect I'm trying to achieve here, once the cell is completely squished vertically, I'd like it not to show itself in the view again, but the deleteItemsAtIndexPaths: call causes the cell's "ghost" to show up and then fade out as the method performs its own transform and opacity animation.
Am I approaching it completely wrong? Possibly the collection view architecture offers a better way to do what I want, rather than explicitly animating the cell and then deleting it like I'm trying?
I'm hoping I might be able to get a quick answer here rather than have to dig into the documentation too deep.
I asked this question on the Apple Developer Forums, and was informed of the
-finalLayoutAttributesForDisappearingItemAtIndexPath:
method which is part of the UICollectionViewLayout class.
Anyway, by subclassing UICollectionViewFlowLayout and overriding this method, I was able to modify the attributes of the cell I was deleting - in particular, the transform.
One thing to note is that this method gets called not only for the cell being deleted but also for cells being shifted from their position during the relayout. But it's simple enough to write code to keep track of the cell being deleted and only modified its attributes.
Related
I am developing a WPF application that displays complex graphics to a window. I need these graphics to be highly dynamic, meaning that for instance lines can appear, disappear or just be moved at any time (by mouse interaction or programmatically).
Is there a way to achieve that without rebuilding the complete display list every time ? A typical case is that of the plot of a function where the data points can move, but not the rest. The points may be computed on the fly and not remembered in an array, so referring to their graphical representation is tricky.
Code example (on request):
// Add a Line Element
myLine = new Line();
myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
myLine.X1 = 1;
myLine.X2 = 50;
myLine.Y1 = 1;
myLine.Y2 = 50;
myLine.HorizontalAlignment = HorizontalAlignment.Left;
myLine.VerticalAlignment = VerticalAlignment.Center;
myLine.StrokeThickness = 2;
myGrid.Children.Add(myLine);
Update:
Assume I plot three functions. I draw the axis, the legends, the tick marks... and three curves.
Now is there a better way than redrawing everything if I want to remove the second curve ?
Is there a way to achieve that without rebuilding the complete display list every time ?
That's exactly what the MVVM pattern in general, and WPF templates together aim to provide. Specifically, you don't describe much of what you're trying to achieve except for this:
lines can appear, disappear or just be moved at any time
To me that sounds like two model classes, one for points (X and Y coordinates) and one for lines (that hold references to two points). The point model is observable (ie it implements INotifyPropertyChanged correctly and fully), while the line model should never change.
Coming to your view model, what you have is a collection of entities (something like IEntity), from which both models are derived. So simply a read only property of type ObservableCollection<IEntity>. When you want to move a point from a line, simply update its X and/or Y coordinate. Want to add a line? Add the points and the line connecting them to your collection. Same for deletion. Easy!
And the last part, your view. This holds an ItemsControl bound to your view model collection, with the panel template set to <Canvas/> (to be able to set location directly) and <DataTemplate> resources, one for each type of model you have:
Lines are simple, they're simply drawn in place by binding their four location properties to the X and Y coordinates of your two point models in your line model.
Points are where you get fancy, their purpose is to provide manipulators to move them around, ie a user control that responds to drag events for example and that updates its bound properties. Because lines hold references to your points, simply updating the point directly will reposition both the affected point visually, as well as the line that connects it.
I have to create a schedule control using WPF XAML for displaying items with duration as little as 1 seconds and potentially as large as couple of hours.
First thing which seems clear is that I will need some kind of zoom capability, for drilling into the items with very short durations and zooming out to view the items with very large durations.
I have a UI style in mind where by when the control is zoomed to view the large items, any small duration items which generally occur between the large duration items are some how collected in to a 'bucket' which has a height >= a minimum display height. So that I can perhaps hover a mouse over this bucket and get a tool tip which renders the contained items into a time ordered list view.
I am at a loss as to where to begin. I feel that I could perhaps achieve something in the Measure/Arrange overrides of a custom Panel. But I think this could adversely affect render performance. If I were to do it this way, I guess I would need to inject a 'bucket' view into the panels children collection, but that would break the use of ItemTemplate in my Custom Items Control?
My second though is that I create a custom observable collection which has a bindable Scale property which then organises the collection into 'buckets' of a duration large enough to be displayable.
Does anyone have any thoughts on how I should best approach this problem?
I made a similar sounding control which was actually used for inputting employee time shifts. It uses a simple data type in an ObservableCollection data bound to a ListBox.ItemsSource property. I defined a DataTemplate to design the look of each time segment in the UI. It looks like this:
There are certain ComboBoxes along the top which, when changed, change the number of items in the collection. So for example, when the Minutes/segment ComboBox value is changed to Thirty, twice the number of (same sized) items appear in the UI, each now relating to thirty minutes, not sixty. This is done by simply calling a method that repopulates the collection from the relevant property setters. Here is an example:
public TimeSegmentDivision MinutesPerSegment
{
get { return minutesPerSegment; }
set
{
minutesPerSegment = value;
InitializeTimeSegmentsRowViewModels();
NotifyPropertyChanged("MinutesPerSegment");
}
}
private void InitializeTimeSegmentsRowViewModels()
{
if (DayCount == 5) AdjustStartDate(); // (DayCount == 5 is 'Monday - Friday')
DateTime tempDate = Date;
AllTimeSegments = new TimeSegmentsCollection();
for (int m = 0; m < DayCount; m++)
{
TimeSegmentsRowViewModel viewModel = new TimeSegmentsRowViewModel();
viewModel.TimeSegments = InitializeTimeSegments();
AllTimeSegments.Add(viewModel);
date = Date.AddDays(1);
}
NotifyPropertyChanged("AllTimeSegments");
date = tempDate;
}
The specifics of the InitializeTimeSegmentsRowViewModels method is unimportant here, but you should be able to get the right idea from this example.
I worked on custom schedule for Silverlight. It has similar layout loading process so I would try to answer.
I guess your main problem lies in the 'time bar'... Stop thinking about schedule as the big consistent control, it's some pack of custom controls. Take a look on grid. Such controls has a lot peaces.
At start we have to solve first problem - time bar. Right implementation will lead you to painless future))) So time bar it's some control that contains total time, or duration of the employee labors(this value can be set by user). And such thing like step , exactly step and duration will tell you size and position of labors. For time bar control step will tell where are visual ticks should be displayed. I mean time bar will look like rulers, but with time values instead of inches. So we are getting next problem - how to translate time into pixels? Unfortunately I didn't found best solution - I assumed 1min is equal to 1.6 pixels It was perfect for me... But you will have dynamic value that will dynamically increase or decrease pixel length of step. So we have total duration in time, we can convert it into pixels length. But total length could be much bigger then available size. So, now we have another time bar property - visual duration...
okay, I guess you understand my way of thinking... Changing pixel size of step you will get perfect zoom(not step time size, but pixel length).
And yes, you are on the right way if you want create custom panels with overrated measure\arrange methods. And don't worry about item template. It's easiest part. Your parent schedule control will have items source property of object type. All your labors will be content controls....
I have stack panel with custom controls in it. User can add or remove the items.
I have attached MouseDragElementBehavior to each item. So now user can move them within the stack panel.
However the items now are arranged on arbitrary manner. Is a mess really. They stay where the user left them.
What I need now is to make them to be stacked as the stack panel supposed to be... Nicely one by one...
So I need to simply let user change the order of items by using drag / drop operation but items has to be stacked precisely.
There is DragFinished event, but I dont really see how the Behavior moves items. I thought it is Margin it changes but margins stays 0... I dont know what to do next.
Appreciate little help.
MouseDragElementBehavior does its work using a Transform on the RenderTransform property of the attached element (i.e. the one the behavior is applied to) (the exact type of transform depends on the state of the RenderTransform property but it will either be a TranslateTransform or a MatrixTransform).
The transform is not reset when you finish dragging (nor would you want it to be because the element would snap back to its position in the StackPanel when you started dragging) and this is why the element stays where "the user left them".
To change the items position in the StackPanel in the way you are talking about you will have to use the DragFinished event. What you probably want to do is work out where in the StackPanel the element will end up (as a result of the drag, i.e. which element in the panel it will "push" down) then create an animation to animate the element from its current position (where the user released the drag) to that final position in the StackPanel and another animation to move the "pushed" element in to its new position in the StackPanel (VisualTreeHelper can probably help here (I think)). Once those animations are finished just set the new index within the StackPanel for each item and remove the RenderTransform translation.
In processing a group of items, I wanted to display a unified image of the status of the group, so I essentially made a Grid of a number of progressbars with transparent backgrounds and various colored foregrounds all at the same cell.
I'm running into some transparency artifacts (purple bar is actually purple under the green, and sometimes it draws over the top, etc) and it just seems a bit wasteful. So, I decided to make my own, but now I've got a bit of paralysis on how to do it. Do I use the DrawingContext in FrameworkElement's OnRender, or is there something simpler? Is there a set of general rules when it comes to making your own control?
I pondered switching to a pie chart since those are easy to come by, but its high time I did something not off-the-shelf.
Thanks!
I'm not quite sure how you intend the progressbar to combine different progresses, but if say the furthest along progress is at the bottom of the z-index and the least along progress is at the top, then I'd do something on the lines of this:
1) I would probably create a user control for this new progresbar.
2) It would have a property called NumberOfProgresses, that is tied with an array containing status of said progresses.
3) Each progress would be represented by a Border item (or perhaps something more suitable up the visual tree), because it's a simple wpf control with a background property. The background property would be set to nice a looking progress style and the progress color can be bound in the style to say the border's borderbrush property. Making it easy to set the color of the progress.
4) The user control would have a method UpdateProgress which takes the percentage value and the index of the progress in the array as parameters.
5) As progresses are updated you can either, just calculate the appropriate width (user control actual width * percentage) for the border and play around with the Z index to get it displayed at the top/bottom, or stack the borders horizontaly, set the least along progress as first, then for the rest of the progresses you'd have to substract previous progresses lengths to get the same effect.
This way there would be no transparency induced artifacts and no OnRender()...
Mind you, in WPF there should be no reason to mess with OnRender this and OnRender that, like it was required in WinForms with OnPaint.
Just set up the elements via code to get the look you want, and let WPF do it's rendering ;)
I can imagine one problem with this user control though. You'd have to provide feedback to the user as to which color belongs to which progress. But that would probably take you back to square one, meaning it's better/simpler to just display multiple progressbars.
A client has asked for a display to flick over like an airport display screen, ie each row flicks over when information changes.
I am not sure which is the best control to use, or the method of getting each row to transform one after the other.
any suggestions woul b gratfully accepted
John
Here's what I would do in general concept..
Make a regular panel of, say 50px high. (This is arbitrary but this panel just holds the size in place so the control doesn't shrink with its contents.)
Create a panel inside that one that will be the 'animated' panel.
When it's time for information to animate, create a storyboard that uses a transformation to "stretch" the height down to 0, change the content to the updated information, then tranform stretch the height back to 50px. This will create the illusion that the panel is flipping over.
If you make this a user control, then you could simply add however many "rows" you needed of this control to a StackPanel to make your screen.
The best way of representing this effect easily is to randomize the text during the change.
Patrick Long implemented this effect as a custom animation here