I've created a a custom activity designer, but I wanted to have control over the 'header' part (that by default shows the ModelItem.DisplayName), and as far I know, the only way to achieve this is to derive the designer from WorkflowViewElement rather than ActivityDesigner. So, that seems to work and all is well. Except, I want to support the new Annotations feature of WF4.5.
Given that when you add an annotation to a workflow element on the design surface it adds an icon to the element you are annotating + the actual annotation element, it seems clear that any custom WorkflowViewElement has to have some extra stuff in it to support this behaviour. Can anyone help?
Thanks
As I thought, annotations are saved within the workflow definition as an attached property. Here's what an annotation looks like in the workflow's xaml:
<Sequence
xmlns:derp="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation"
derp:Annotation.AnnotationText="This is an annotation!">
See, just like any other attached property. Except it isn't. Its an attached workflow property, not an attached DependencyProperty. That means it works through the attached property service as well as the Annotation class. Getting and setting the annotation text on a ModelItem is trivial (and covered below).
Its actually not that hard to support annotations. As long as you don't mind your UI looking like crap. Here's a quick and dirty implementation.
In the UI, add some controls for holding and editing the annotation text
<sap:WorkflowViewElement
x:Class="AnnotationSupport.MyActivityDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:ann="clr-namespace:System.Activities.Presentation.Annotations;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
x:Name="root"
MinWidth="100"
MinHeight="100">
<Grid Background="red">
<Grid.RowDefinitions>
<RowDefinition
Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Expander
IsExpanded="False">
<!-- HERE SHE BLOWS -->
<TextBox
Text="{Binding ModelItem.AnnotationText}" />
</Expander>
<TextBox
Grid.Row="1"
Text="{Binding ModelItem.Text}"
Margin="10" />
</Grid>
</sap:WorkflowViewElement>
Any time the Annotation text changes, the ModelItem's PropertyChanged event fires, just like any other property. And if you want to grab it from code, the simplest way is to cast the ModelItem to a dynamic:
private void SetAnnotationLol(string newValue)
{
if(ModelItem != null)
((dynamic)ModelItem).AnnotationText = newValue;
}
Now, if you want to create a nice UI like the Fx Activities have... well...
I'll leave it up to you to create a custom Adorner to handle display and editing of the annotation. Which actually isn't as hard as it first looks. If you haven't done one yet, here's your opportunity.
Related
Xamarin.Forms 5.0.0.2012, Visual Studio 2019 for Mac 8.10.16
I am encountering a problem (on both iOS and Android, on both emulators and physical devices) trying to call a Converter in the XAML describing a new screen in our app. This is the markup:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:internal="clr-namespace:Views.Shared"
x:Class="Views.EditPage">
<ContentPage.Resources>
<internal:PictureNameToImageSourceConverter x:Key="PictureNameToImageSourceConverter" />
</ContentPage.Resources>
...
<CollectionView
x:Name="picturesView"
ItemsLayout="HorizontalList"
HeightRequest="90">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5">
<Image
HeightRequest="80"
Source="{Binding Path=., Converter={StaticResource PictureNameToImageSourceConverter}}" />
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
The CollectionView.ItemsSource value is set in code to a List<string> instance. When the List has no items, the screen displays correctly. When it does have items the app crashes as the screen appears. If I launch the app attached to the VS debugger, the screen freezes before the crash, and I never get any information.
This converter is well tested and is used several other places in the app with no problem. When I replace the Image with <Label Text="{Binding Path=.}"/> the text items are displayed as expected, so it doesn't look like a binding or Path syntax error.
Is there something I'm not seeing or not aware of in the markup that's causing this? Or can anyone suggest further debugging I haven't thought of?
UPDATE
A breakpoint on the very first line of the converter was never reached.
EDIT in response to comments:
From the EditPage codebehind:
public partial class EditPage : ContentPage
{
internal List<string> PictureNames;
...
protected override void OnAppearing( )
{
base.OnAppearing();
picturesView.ItemsSource = PictureNames;
}
The PictureNames property is actually set by a different Page to which one navigates from the EditPage:
private void saveSelection_Click(object sender, System.EventArgs args)
{
creator.PictureNames = new List<string>();
foreach (SelectableItem<Picture> item in pictureItems)
{
if (item.IsSelected)
{
creator.PictureNames.Add(item.Item.PictureName);
}
}
Navigation.PopAsync();
}
pictureItems is a List acting as a ListView.ItemsSource on that screen, where pictures are selected or unselected.
UPDATE
After much setting of breakpoints, I've determined that the line picturesView.ItemsSource = PictureNames; is where the crash happens. It seems odd that it only happens when the template is showing an Image, but not a Label, seeing that the converter is never actually called.
UPDATE
The trick of adding the delay did get me to the breakpoint. And what I found is more puzzling than ever: The value parameter being passed to the Convert method of our converter is null. This is the case whether coming back from the picture selection screen, or if I set the bound list in response to a Button rather than in OnAppearing, or if I just set it right in the page constructor.
In addition, when setting a breakpoint on the crashing line, when the display element in the template is a Label everything is as expected, but when the display element is an Image the debugger freezes when trying to look at those values. The problem is apparently something about the fact of calling a converter in this precise situation.
I tested that by adding a different converter to the template:
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5" HeightRequest="{Binding Path=., Converter={StaticResource PictureNameToHeightConverter}}">
<Label Text="{Binding Path=.}" />
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
Exactly the same result: The value parameter passed to the Convert method is null even though the same bound value in the same template instance is displayed in the Label. If I set a breakpoint on the line assigning the ItemsSource property as before, the debugger freezes.
UPDATE
Finally beginning to suspect that I'm triggering some corner case bug in the framework, I replaced the CollectionView with a CarouselView, and it works correctly:
<CarouselView
x:Name="picturesView"
HeightRequest="90">
<CarouselView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5">
<Image
HeightRequest="80"
Source="{Binding Path=., Converter={StaticResource PictureNameToImageSourceConverter}}" />
</Frame>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
I'm adding this as an update to the question rather than as an answer because I still don't actually know the explanation.
I've seen some odd behavior when updating page details in OnAppearing.
Hard to put a finger on exactly when there will be a problem, but the fact that you navigated to another page, so this is OnAppearing during the "back" from that other page is probably a factor.
Try this:
protected override void OnAppearing( )
{
base.OnAppearing();
Device.BeginInvokeOnMainThread( async () => {
// Let the page appear on screen, before setting ItemsSource.
await System.Threading.Tasks.Task.Delay(200);
picturesView.ItemsSource = PictureNames;
});
}
At minimum, this should allow Xamarin to reach the breakpoint in the converter.
——————-
UPDATE
I don’t see any obvious flaw in the code you’ve posted.
You’ve narrowed it down to the line it crashes on.
Yet a breakpoint at start of converter is never reached.
As you say, this is a puzzling combination of facts.
Here is a test to do:
Put breakpoint on the line that crashes.
Copy the values in the list, to a text editor.
Add a button on the current page, that when pressed, fills the list with those same strings, hardcoded as a literal, then sets the ItemSource.
Start over, but this time press the button - Does this work or crash?
That is, remove all the complexity of going to another page, querying values, returning to this page.
I bet this will work. Then you’d have the best situation for debugging: a case that works vs. one that doesn’t.
After that, its “divide and conquer”. Start making the working one more like the broken one, and/or vice versa, until the culprit is identified.
You could try to pass data with the ways below when you do the navigation.
One way is to set the BindingContext to the page which you want to navigate to.
Use the Navigation.PushAsync to navigate the page and reset the BindingContext before the page navigated. You could check the thread i done before for more details. Xamarin Public Class data how to acess it properly
Or you could try to binding to a path property using MVVM.
<Image HeightRequest="80" Source="{Binding path, Converter={StaticResource PictureNameToImageSourceConverter}}" />
Another way is to pass the data to the page which you want to navigate through a Page Constructor.
For more details of this way, check the MS docs. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical#passing-data-through-a-page-constructor
While it would be interesting to know why the CollectionView binding isn't behaving as expected in this specific situation, the number of hours spent trying to figure it out has definitely passed the point of diminishing returns, so I just added the images the old-fashioned way. I replaced the CollectionView with a StackLayout:
<StackLayout x:Name="picturesLayout" Orientation="Horizontal" />
And then just added framed images "by hand" in the OnAppearing method:
picturesLayout.Children.Clear();
foreach (string pictureName in PictureNames)
{
picturesLayout.Children.Add(new Frame { Padding = 5, HasShadow = false, Content = new Image { Source = ImageManager.ReadFromDisk(pictureName), HeightRequest = 80 } });
}
(ImageManager handles chores like keeping track of the full path to the appropriate directory.)
I am using Editor control and wish to wrap long messages (Like Whatsapp).
I have explored few sites and got nothing . If this feature is not yet in Xamarin Forms , can I know any alternative for the same ?
My XAML
<Editor Grid.Row="0" Grid.Column="0" Text="{Binding TextMessage}"
TextColor="{StaticResource StartColor}" HorizontalOptions="FillAndExpand"
FontSize="14" TextChanged="Editor_TextChanged"
FontFamily="{StaticResource DefaultFont}" IsEnabled="True"/>
OUTPUT-
I would first suggest you to use AutoSize property of Editor, so if you want your editor to automatically resize depending on the text length, you should set it to TextChanges, otherwise set it to Disabled(this is default value).
Next thing, if you know that the Editor will be used for chat, you can also set Keyboard property to Chat:
<Editor Text="Enter text here" AutoSize="TextChanges" Keyboard="Chat" />
If I am not mistaken, The Editor control has a property for this called the AutoSize Property which takes the EditorAutoSizeOption enum as an input. Available options are TextChanges and Disabled.
<Editor AutoSize="TextChanges"...
If for some reason this does not work for you then you can use the solution in this forum discussion. by ionixjunior.
For those who are still facing issues, I have a case where the line does not wrap as long as there is no break line (Enter). Solution was to change parent layout from StackLayout to Grid. With grid, now it works.
If you want the height to change dynamically when you have more lines, you can use the above solution:
AutoSize="TextChanges"
I am using Prism to build xamarin forms apps.
I have never used Partial Views and cannot find any examples out there.
Can somebody point me to an example,so that I can see if fits what I am trying to achieve?
many thanks
From the Docs:
The concept of a Partial View is to support a custom layout which may
be reused across multiple pages, and eliminate ViewModel logic
duplication by allowing that custom layout to rely on its own
ViewModel. To use a Partial View you must set the
ViewModelLocator.AutowirePartialView property with a reference to the
containing page as shown here. You should not set the
ViewModelLocator.AutowireViewModel property on the Partial View unless
you are explicitly opting out as setting this property to true
directly may result in the ViewModel being incorrectly set.
Example:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AwesomeApp.Views"
xmlns:prism="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
x:Name="self"
x:Class="AwesomeApp.Views.ViewA">
<StackLayout>
<local:AwesomeView mvvm:ViewModelLocator.AutowirePartialView="{x:Reference self}" />
<Entry Text="{Binding SomeValue" />
</StackLayout>
</ContentPage>
This is probably a very simple question, but at this time I have myself so confused I can't see the answer. Simply put, I have a window that contains a content control. I'm using Caliburn.Micro's conventions to "locate" the view.
The window looks like this:
<Window x:Class="Views.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox/>
<ContentControl x:Name="MyViewModel" Height="Auto" Background="Blue"/>
</Grid>
</Window>
The view itself is successfully found, and the screen displays as I expected. However, MyViewModel needs to make a service call to get information based on what is typed into the text box.
So, what I can't seem to figure out is how one would pass that information from the text box to the view model. I've thought of several options, but they all seem to be too much work which makes me think that I'm missing something simple.
Thanks a lot
Like you said there are a number of things you can do:
You could expose a property on MyViewModel and set it within
MainWindowView.
You could use the EventAgregator, publish an event from the
MainWindowView and subscribe to that event from MyViewModel.
Using MEF you could inject a shared resource between the two
ViewModels, set it in MainWindowViewModel, and be able to access it
from MyViewModel.
Suppose I have entity graph like
People ->Student
then in xaml, I have following kind of binding(People is property of VM):
<TextBox Text="{Binding People.Name, Mode=TwoWay}" />
<TextBox Text="{Binding People.Student.StudentNo, Mode=TwoWay}" /> <!-- this bounding is not working -->
in VM, implementing IEditableObject. I have some code like:
public void BeginEdit()
{
((IEditableObject)this.People).BeginEdit();
((IEditableObject)this.People.Student).BeginEdit(); //this code not working
//....
}
When runing the app, all data bound to People is fine.
All data bound to Student is not working.
How to fix it?
I'd guess that the Student property is null on the client side. You need to add the [Include] attribute to the Student property on the server-side, so that it gets taken across to the client side by RIA services. You may also need to add an include for your server-side to retrieve it from the database, depending on how your data-access is written.