I try to show on a ContentPage an ObservableCollection of type Activity (class I created). This one contains a Title (string) and a Map (xamarin.forms.maps) :
Map myMap = new Map();
myMap.IsShowingUser = true;
activity.Map = myMap;
obActivities.Add(activity);
Here is my XAML :
<ListView x:Name="listActivities" HorizontalOptions="StartAndExpand" VerticalOptions="FillAndExpand"
SeparatorColor="LightGray" SeparatorVisibility="Default" HasUnevenRows="True"
ItemsSource="{Binding obActivities}" CachingStrategy="RecycleElement"
ItemSelected="ListActivities_ItemSelected" SelectedItem="{Binding selectedActivity,Mode=TwoWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Padding="5" VerticalOptions="FillAndExpand">
<ContentView Content="{Binding Map}"></ContentView>
<Label Text="{Binding Title}" FontSize="12" TextColor="Gray"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The Title is well displayed.
I tried a lot of things (may be everything I can ^^) to show the Map but nothing works. The last thing I tried before this post... <ContentView Content="{Binding Map}"></ContentView> but no result.
EDIT :
I also tried to add my map control onto the XAML like :
<map:Map>
<map:Map.MapElements>
<map:Polyline>
<map:Polyline.Geopath>
</map:Polyline.Geopath>
</map:Polyline>
</map:Map.MapElements>
</map:Map>
But I don't know where I have to add the polyline's position from my VM.
May be you can help me to find the right XAML ...
I recommend managing a collection of UI elements (here, Maps) from inside the View or Page. I would move all the ViewModel code into the Page's code behind .xaml.cs file. There is still a "model" for each item ("Activity"), but no longer a ViewModel corresponding to the Page itself.
NOTE: This is an "impure" approach, as Activity is a mixture of model and UI information. Nevertheless, it is simpler code to do it this way. So I won't attempt to show a "pure" solution. In a simple case, the complications of doing so outweigh the "maintenance" benefit [easier to make changes without causing a bug] of keeping view and model separate. If this causes some problem later, refactor at that time.
Then BindingContext becomes the page itself:
public MyPage() {
InitializeComponent();
InitMyData();
BindingContext = this;
}
public ObservableCollection<Activity> obActivities { get; set; } = new ObservableCollection<Activity>();
void InitMyData() {
Map myMap = new Map();
myMap.IsShowingUser = true;
activity.Map = myMap;
obActivities.Add(activity);
}
The Map may lack a height when it is created. StackLayout may have given it "0" height by default. Specify some height:
Map myMap = new Map();
myApp.HeightRequest = 100;
Having a "hardcoded" height (e.g. 100) also helps speed up the initial layout of the page.
Related
I have a problem with my ListView. I bind the ItemsSource to my ViewModel but I want items that have a certain property set to null to be completely collapsed (as in the whole item is collapsed, not just the control that is bound the the property that might be null). I have a converter that works but the ListView still seems to reserve at least some space for items that are collapsed. This results in my ListView having ugly "holes". (Please note that the holes do not take up as much space as the visible items but they do take up some space and the "invisible" items are even clickable...)
My problem is pretty much the same as the one stated here: Not showing items with Visibility=Collapsed in Windows 8.1 GridView
The only thing that bothers me is that the OP of the linked question says, that for him it's working fine in Windows Phone but not in Windows. Well for me it's not working in Windows Phone. Any suggestions what I could do?
The Code:
<DataTemplate x:Key="UpcomingEpisodesTemplate">
<Grid Visibility="{Binding Upcoming, Converter={StaticResource EpisodeVisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
....The rest of the template which is not important...
</Grid>
</DataTemplate>
Thanks!
I have searched for 2 days to collapse items without the remaining place holder from the container style and this seems to work (WinRT 8.1).
(Key_Up Event from TextBox)
foreach (YourType element in TheListViewZoomedIn.Items)
{
if (!element.YourProperty.Contains(ListSearchBox.Text))
(TheListViewZoomedIn.ContainerFromItem(element) as ListViewItem).Visibility = Visibility.Collapsed;
}
It might not be super elegant or viable for huge lists but it does the job and offers tons of control. Just make sure to set the visibility again where you need to.
I just wanted to share this - it might help others in the future,
best regards,
Stefan
You could try convert whatever you binding to ItemsSource. Like this:
<ListView
ItemsSource={Binding MyList, Converter={StaticResource MyListConverter}}>
With converter like this.
public class MyListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return ((IEnumerable<MyType>)value).Where(x => x.MyProperty != null);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
Edit with ObservableCollection.
public class MyListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var originalObservable = (ObservableCollection<MyType>)value;
var collection = new ObservableCollection<MyType>();
collection.AddRange(originalObservable.Where(x => x.MyProperty != null));
originalObservable.CollectionChanged += delegate { collection.Clear(); collection.AddRange(originalObservable.Where(x => x.MyProperty != null)); };
return collection;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
In case if it helps, you can solve this issue with extra spaces of Collapsed ListViewItems by changing ItemContainerStyle to your custom style (edit a copy of default style) and change:-
<Setter Property="MinWidth" Value="{ThemeResource ListViewItemMinWidth}"/>
<Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}"/>
To
<Setter Property="MinWidth" Value="0"/>
<Setter Property="MinHeight" Value="0"/>
I've got a Flyout embedded within an AppBarButton like so:
<AppBarButton x:Name="appbarbtnOpenPhotosets" Icon="OpenFile" Label="Open Existing Photoset[s]" AutomationProperties.Name="Open File" Tapped="appbarbtnOpenPhotosets_Tapped" >
<Button.Flyout>
. . .
</Button.Flyout>
</AppBarButton>
I want to, under certain circumstances, first present the user with an opportunity to rename a file prior to seeing the Flyout. I tried seeing if that would work like this:
async private void appbarbtnOpenPhotosets_Tapped(object sender, TappedRoutedEventArgs args)
{
// Want to conditionally postpone the operation
bool myBucketsGotAHoleInIt = PhotraxUtils.GetLocalSetting(CAINT_BUY_NO_BEER);
if (myBucketsGotAHoleInIt)
{
MessageDialog dlgDone = new MessageDialog("Can you see me now?");
await dlgDone.ShowAsync();
args.Handled = false; // <= adding this made no difference
}
}
This works, in that I see the "Can you see me now?" dialog, but that prevents the Flyout from flying out. A Flyout that doesn't fly out is no more useful than a flying squirrel or fish that doesn't motate through the air.
So how can I temporarily suppress my flyout but then call it forth? The Flyout does not have an Open() method...Is there some other way to invoke it?
Flyouts attached to Buttons open automatically when you click the control.
If you don't want it to open automatically, you need to attach it to another control.
Example taken from official documentation:
<!-- Flyout declared inline on a FrameworkElement -->
<TextBlock>
<FlyoutBase.AttachedFlyout>
<Flyout>
<!-- Flyout content -->
</Flyout>
</FlyoutBase.AttachedFlyout>
</TextBlock>
Then you can show the Flyout whenever you want, calling FlayoutBase.ShowAttachedFlyout() and passing the FrameworkElement casted value of your control.
FlyoutBase.ShowAttachedFlyout(frameworkElement);
So, in your case:
async private void appbarbtnOpenPhotosets_Tapped(object sender, TappedRoutedEventArgs args)
{
// Want to conditionally postpone the operation
bool myBucketsGotAHoleInIt = PhotraxUtils.GetLocalSetting(CAINT_BUY_NO_BEER);
if (myBucketsGotAHoleInIt)
{
MessageDialog dlgDone = new MessageDialog("Can you see me now?");
await dlgDone.ShowAsync();
// New code
FlyoutBase.ShowAttachedFlyout((FrameworkElement)sender);
}
}
If you can't change the control, you should able to use the code I posted with Buttoninstead of TextBlock. I'm not sure about this, but you can try.
Recently I was using LongListMultiSelector(LLMS) in WPToolkit and now facing a quite strange bug. That is, when the ItemsSource of the LLMS has exactly 3 items. Then the second one's view doesn't get refreshed, while the other two do. And the LLMS is all right when the count is other than 3. I wrote a small App to test the situation. In this test, a LLMS is binding to an ObservableCollection<ItemViewModel>. Here is the code of ItemViewModel:
public class ItemViewModel : INotifyPropertyChanged
{
public ItemViewModel()
{
time = DateTime.Now;
}
private DateTime _time;
/// <summary>
/// Property for test use
/// </summary>
public DateTime time
{
get { return _time; }
set
{
if (_time != value)
{
_time = value;
RaisePropertyChanged("time");
}
}
}
#region INPC Impl
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string pName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
#endregion
}
And my LLMS is set like this:
<phone:PhoneApplicationPage.Resources>
<local:ItemToStrConverter x:Key="cvt"></local:ItemToStrConverter>
</phone:PhoneApplicationPage.Resources>
......
<toolkit:LongListMultiSelector Height="300" Name="llms">
<toolkit:LongListMultiSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource cvt}}"
Style="{StaticResource PhoneTextLargeStyle}"></TextBlock>
</DataTemplate>
</toolkit:LongListMultiSelector.ItemTemplate>
</toolkit:LongListMultiSelector>
The ItemToStrConverter is something that converts an instance of ItemViewModel to string (time.ToString()).
Please do notice that the Text of TextBlock in the DataTemplate is not binding to time. Instead, it is binding to the ItemViewModel instance directly.
So let's say if the LLMS is on the MainPage, and I go to some other pages to modify an ItemViewModel instance's time and go back to MainPage, this will cause the LLMS to be loaded and new one or more ItemToStrConverter to convert. If I have more or less than 3 items, everything is fine, all the instances will be converted properly. However, when I have 3 items, the second one does not get converted. I made a breakpoint in the Convert method, and the bug is occuring because this method was only called twice (to the 1st and 3rd instance). It seems that the LLMS simply ignores the 2nd instance.
Has anybody met this problem before?
For some reason, my LongListSelector is passing its inherited DataContext into my ItemTemplate converter causing the list items to display their default .ToString() value instead of my template.
I initially had the ItemTemplate defined as a Button control with a Template bound to a property of the items bound to the LongListSelector and that used an IValueConverter to grab the proper ControlTemplate:
ParentViewModel.cs
...
List<ChildViewModel> ChildViewModels { get; set; }
...
ChildViewModel.cs
...
MyEnumType MyEnumType { get; set; }
...
MainPage.xaml
<phone:LongListSelector x:Name="MyLongListSelector"
IsGroupingEnabled="False"
ItemsSource="{Binding ChildViewModels}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Button Template="{Binding MyEnumType, Converter={StaticResource MyConverter}}" DataContext="{Binding}"/>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
This worked fine, but I'm pretty sure I don't need to define a Button as the data template since I should be able to capture anything I need based on the selection event of the LLS.
So, trying to do it right (in my eyes; this is my first real WP8 App), I changed all my Templates from ControlTemplates to DataTemplates and updated my MyConverter to return DataTemplate objects based on the ChildViewModelProperty bound to the ItemTemplate:
public class MyConverter : IValueConverter
{
public object Converter(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
MyEnumType enumType = MyEnumType.DefaultType;
Enum.TryParse<MyEnumType>(value.ToString(), out enumType);
DataTemplate template = null;
switch (enumType)
{
case MyEnumType.Type1:
template = (DataTemplate)App.Current.Resources["MyEmumTypeTemplate1"];
break;
case MyEnumType.Type2:
template = (DataTemplate)App.Current.Resources["MyEnumTypeTemplate2"];
break;
default:
template = (DataTemplate)App.Current.Resources["MyEnumTypeTemplateDefault"];
break;
}
return template;
}
...
}
Then I updated my LongListSelector to bind the ItemTemplate directly to my ChildViewModelProperty of type MyEnumType with my MyConverter (declared in App.xaml as a static resource) converter as so:
<phone:LongListSelector x:Name="MyLongListSelector"
IsGroupingEnabled="False"
ItemsSource="{Binding ChildViewModels}"
ItemTemplate="{Binding MyEnumType, Converter={StaticResource MyConverter}}"
</phone:LongListSelector>
Now, when I view my app in the designer or in the emulator, my LongListSelector outputs a list with all of my elements presented as their .ToString() value (i.e. their fully qualified name).
What I've found is that ParentViewModel is being passed into MyConverter instead of each of the ChildViewModel.MyEnumType values. When I take out the specified Binding PropertyName (MyEnumProperty) and step through the debugger, value is of type ParentViewModel, the inherited DataContext of the LongListSelector. When I enter my desired Property name for the Binding, it simply fails the binding and doesn't even get into the Converter.
I saw a previous article that mentioned something about the LLS not handling a template change, but it had a comment (in 2011) that it's been fixed. I figured since it's 2013, it shouldn't be an issue anymore.
Am I missing something obvious, or do I need a go a little bit further? Or did I do it right in the first place with the Button?
Thanks for any help!
The issue is that when your binding is evaluated in the context of the LongListSelector as a whole and at that time the data context is the ChildViewModels. It is not reevaluated when implementing the individual item.
The approach I'd take to address your problem would be to implement a custom DataTemplateSelector that can choose the datatemplate based on the enum value.
<phone:LongListSelector x:Name="MyLongListSelector"
IsGroupingEnabled="False"
ItemsSource="{Binding ChildViewModels}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<MyDataTemplateSelector Content="{Binding}">
<MyDataTemplateSelector.Type1>
<DataTemplate>
// ... your content here
</DataTemplate>
</MyDataTemplateSelector.Type1>
<MyDataTemplateSelector.Type2>
<DataTemplate>
// ... your content here
</DataTemplate>
</MyDataTemplateSelector.Type2>
<MyDataTemplateSelector.Default>
<DataTemplate>
// ... your content here
</DataTemplate>
</MyDataTemplateSelector.Default>
</MyDataTemplateSelector>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
There's an example of how to implement such a selector at http://www.geekchamp.com/articles/implementing-windows-phone-7-datatemplateselector-and-customdatatemplateselector
I'm trying to create an ImageToggleButton for my WinRT project that changes it's image in respect to its IsChecked property.
In the designer of Visual Studio 2012 and Visual Studio Express for Windows 8 the ToggleButton behaves as expected, but during run time the image does not get displayed.
Any ideas?
XAML:
<Page.Resources>
<local:BooleanImageConverter x:Name="BoolImgConverter"/>
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ToggleButton HorizontalAlignment="Left" Height="200" Margin="260,205,0,0" VerticalAlignment="Top" Width="833" Background="Yellow">
<Image Source="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Parent.IsChecked, Converter={StaticResource BoolImgConverter}, ConverterParameter=Assets/chkMusic}"/>
</ToggleButton>
</Grid>
CodeBehind:
public class BooleanImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string culture)
{
bool state = (bool)value;
string baseName = (string)parameter;
return string.Format("{0}_{1}checked.png", baseName, state ? "" : "un");
}
public object ConvertBack(object value, Type targetType, object parameter, string culture)
{
return null;
}
}
Code description:
I'd put an Image control into the ToggleButton and bind the image source to the IsChecked property of the containing (parent) ToggleButton using a converter that decorates the given ConverterParameter according IsChecked to provide the string to reference the desired image file.
Sure I could do this in code behind, but I would prefer to do it in XAML as much as possible.
At the moment I'm also not after building a ControlTemplate and I would have tried RelativeSource FindAncestor and other suggestions of this forum, but these dids not seem to work in Windows Store Apps (or am I wrong?).
I'd appreciate any suggestions.
Thanks
(BTW: I tried to add windows-store-app as tag to this post, but could not create this new tag - would someone else do this)
Parent has never worked as you think it would. There are a couple of ways to accomplish what you need.
The first is to keep the RelativeSource binding but remove the Path to IsChecked. This will pass the control to the converter. In it you can get the parent using the VisualTreeHelper.
The second is to name your ToggleButton and bind to it using ElementName binding
<ToggleButton x:Name="MusicToggle" HorizontalAlignment="Left" Height="200" Margin="260,207,-793,-207" VerticalAlignment="Top" Width="833" Background="Yellow">
<Image Source="{Binding IsChecked, ElementName=MusicToggle, Converter={StaticResource BoolToImage}, ConverterParameter=Assets/chkMusic}"/>