XAML: Binding scrollviewer with 2 sources - xaml

Trying to learn xaml designing. I got this picture from internet and use it as a model to practice. There is a problem: When I click one of day buttons, a scrollviewer with a list of day tasks appears. Each task has a list of hours in day, from start time to end time. I don't know whether they are from one source or 2 source (one describes day task and the other one is a list of hours of this task).
I tried with this
public async void ShowTasks()
{
ObservableCollection<DayTask> Data = await App.DataSource.GetData();
DateListBox.ItemsSource = Data;
DayTask daytask = Data.Where(x => x.Day.Date == App.Day.Date).FirstOrDefault();
TasksListView.ItemsSource = daytask.Tasks;
List<HoursModel> HoursList = new List<HoursModel>();
foreach (var simpletask in daytask.Tasks)
{
HoursList.Add(new HoursModel(simpletask.StartTimeHour, simpletask.EndTimeHour));
}
HoursListView.ItemSource = HoursList;
}
This is my xaml code:
<ScrollViewer Height="730" Width="300" >
<ListView
x:Name="TasksListView" Height="730" Width="300"
HorizontalAlignment="Right">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<ListView
Grid.Column="0"
x:Name="HoursListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock
x:Name="ShowedHours"
Text="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Column="1">
<Rectangle
Fill="Pink"
Height="75"
Width="150"
Margin="60,20,0,0"/>
<TextBlock
Text="{Binding Description}"
FontSize="17"
TextWrapping="Wrap"
Margin="80,20,0,0"/>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
I got an error with HoursListView.ItemSource = HoursList;. The detail of this error is
The name 'HoursListView' does not exist in the current context. Does anyone have any suggestions how I can do this ?

You can't access control in DataTemplate directly by it's name. That makes sense because DataTemplate in this case is generated repeatedly for each item within TasksListView.
According to XAML markup posted, you want to have different HoursList for each DayTask. That can be achieved by adding an HoursList property in DayTask class. I'd suggest to use ObservableCollection instead of List because the former has built-in feature to notify bound UI to refresh whenever item added to or removed from collection :
public class DayTask
{
public ObservableCollection<HoursModel> HoursList { get; set; }
.....
}
Then bind HoursListView's ItemsSource to above property :
.....
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<ListView
Grid.Column="0"
ItemsSource="{Binding HoursList}"
x:Name="HoursListView">
.....
</ListView>
</Grid>
</DataTemplate>
.....
Don't forget to populate all HoursList properties before setting DateListBox.ItemsSource.

Related

Xamarin's Buttons issue

I've only recently started Xamarin and I'm having trouble figuring out how to design a few blocks. I'm trying to create some simple buttons to navigate with and it was decent (picture 2). But as soon as I tried binding it to a data, the design basically ran off (picture 1). Any suggestions?
Image of the problem
Image of the attempt
Below are the codes for picture 1:
<ListView x:Name="displayCourses" ItemsSource="{Binding CourseName}">
<ListView.Header>
<Grid HorizontalOptions="Center">
<!--<Button Text="{Binding CourseName}" BackgroundColor="#ea3e15"
CornerRadius="20" WidthRequest="290" HeightRequest="130"
FontAttributes="Bold" FontSize="26" Clicked="NextPage"
/>-->
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Button Text="{Binding CourseName}" BackgroundColor="#ea3e15" TextColor="#fff"
CornerRadius="20"
FontAttributes="Bold" FontSize="26"
/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CS codes linking the data together
protected async override void OnAppearing()
{
base.OnAppearing();
displayCourses.ItemsSource = await firebaseHelper.GetCourses();
}
public async Task<List<Course>> GetCourses()
{
FirebaseClient firebase = new FirebaseClient("https://pra-ece32-default-rtdb.firebaseio.com/");
return (await firebase
.Child("Courses")
.OnceAsync<Course>()).Select(item => new Course
{
CourseName = item.Object.CourseName,
CourseID = item.Object.CourseID,
ExamDate = item.Object.ExamDate,
}).ToList();
}
The width of a row inside the listview will always take up the entire width of the listview itself. There is no way to have extra space between the end of a Row and the edge of the listview, nor would that make any sense.
But you can use Grid to meet your requirement. Please set ListView HasUnevenRow="True" to automatically size row height to fit content.
<ListView
x:Name="displayCourses"
HasUnevenRows="True"
ItemsSource="{Binding courses}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Margin="20"
BackgroundColor="#ea3e15"
CornerRadius="20"
FontAttributes="Bold"
FontSize="26"
HeightRequest="50"
Text="{Binding coursename}"
TextColor="#fff"
WidthRequest="200" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Only capture tap event from button contained in ListViewItem without triggering item click - UWP Windows 10

In my UWP app which uses MVVMLight, I have a button contained in my ListViewItem and when clicked I want to display a flyout and provide additional actions for the specific ListViewItem but whenever I tap on the button, I get an error i.e.
System.InvalidCastException: Unable to cast object of
type 'Windows.UI.Xaml.Input.TappedRoutedEventArgs' to
type 'MyApp.ViewModels.ItemViewModel'.
at GalaSoft.MvvmLight.Command.RelayCommand`1.Execute(Object parameter)
at Microsoft.Xaml.Interactions.Core.InvokeCommandAction.Execute(Object
sender, Object parameter) at
Microsoft.Xaml.Interactivity.Interaction.ExecuteActions(Object sender,
ActionCollection actions, Object parameter)
at Microsoft.Xaml.Interactions.Core.EventTr
I understand to some extend why it's happening but how I can I fix this? The button contained in my ListViewItem data template is obviously being passed to the parent which is the listview and the DataTrigger that's defined in the Listview is capturing it.
All I want to do is display a flyout when this button is clicked to provide additional options.
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="{StaticResource SystemControlBackgroundAccentBrush5}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="4"
Width="90"
Height="90"
VerticalAlignment="Center"
Margin="5,5,0,5">
<Image Width="90"
Height="90"
Source="{Binding Logo}" />
</Border>
<Grid Grid.Row="0" Grid.Column="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Row="1" VerticalAlignment="Bottom">
<TextBlock Text="Id:" FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Subject}" Margin="10,0,0,0" Foreground="White" VerticalAlignment="Center"/>
</StackPanel>
<Button Width="30" Height="30"
Command="{Binding AdditionalInfoCommand}"
Grid.RowSpan="4"
Grid.Column="1"
Margin="0,0,12,0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Stroke="{ThemeResource SystemControlBackgroundAccentBrush}"
Fill="{ThemeResource SystemControlBackgroundAccentBrush}"
StrokeThickness="2">
</Ellipse>
<Image Source="ms-appx:///Assets/Information.png" Width="30" Height="30" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
and my ListView has the following code:
<ListView ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
.... as defined above
</ListView.ItemTemplate>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
Any ideas on how I can resolve this?
UPDATE 1:
I'll re-phrase my question slightly. I've managed to bind the button displayed on the ListViewItem to a specific click event by introducing a RelayCommand (AdditionalInfoCommand) in the item's ViewModel. So now my main problem is the actual error mentioned above which is triggered after AdditionalInfoCommand and I assume it's because it is being captured by the ItemClickCommand. Strange as you would assume that it is using the same ViewModel.
Is there a way to no trigger the itemclick when this button is tapped?
Thanks.
Thierry
I figured it out.
When I tap the button inside my ListView DataTemplate, the button is being passed the Item's ViewModel which is correct but then the tap event is bubbled up back to the parent which is captured by:
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
While there may be a solution to stop the event bubbling, to circumvent the problem, I changed my ItemClickCommand.
It was defined as follows:
private RelayCommand<MyItemViewModel> _itemClickCommand;
public RelayCommand<MyItemViewModel> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<MyItemViewModel>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I simply changed it to:
private RelayCommand<object> _itemClickCommand;
public RelayCommand<object> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<object>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I then changed the ItemClickCommandAction to take in an object instead of MyItemViewModel and now, within the method, I check the type of the ViewModel being passed:
private void ItemClickCommandAction(object currentObject)
{
if (currentObject is MyItemViewModel)
{
//go to next page
}
}

Windows StoreApp XAML: Changing ItemTemplate in GridView based on Data type

I am working on developing Windows Store App using C#/XAML. I have experience primarily in iOS and to some extent Android app development, but not yet comfortable with C#/XAML world.
Here is my issue in GridView based page (Based on the nice template VS2012 generates).
I have a gridview and its collection is bound to a data retrieved from network and it works fine.
But I want to change the grid item depending on the data.
For example: I have files and folders that I would like to show using different grid view items.
My Question: How would I use a different DataTemplate for the ItemTemplate depending on the data? For example, for "Folders", I will have only one textblock which is vertically centered and for File, I will have the 2 textblocks and visually different.
Am I going the right path or should I be doing completely different?
The XAML Portion is
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="3"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource FileEntriesTemplate}"
ItemClick="ItemView_ItemClick"
IsItemClickEnabled="True"
SelectionMode="None"
IsSwipeEnabled="false">
The Template is
<DataTemplate x:Key="FileEntriesTemplate">
<Grid HorizontalAlignment="Left" Width="400" Height="80" Background="Beige">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Image}" Stretch="Uniform" Grid.Column="0" Margin="10,0,0,0" AutomationProperties.Name="{Binding Title}"/>
<StackPanel Orientation="Vertical" Grid.Column="1" Background="Transparent">
<TextBlock Text="{Binding Title}" Foreground="Black" Style="{StaticResource LargeTitleTextStyle}" Margin="20,20,10,0"/>
<TextBlock Text="{Binding Subtitle}" Foreground="gray" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="20,10,0,30"/>
</StackPanel>
</Grid>
GridView exposes this through the ItemTemplateSelector property which is a class you can create that inherits from DataTemplateSelector. An example would be that I have a GridView that has Issues and Repositories bound to it and want to use different data templates for each.
My data template selector looks like:
public class IssueSummaryTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return item is IssueGroupViewModel ? IssueTemplate : RepositoryTemplate;
}
public DataTemplate RepositoryTemplate
{
get;
set;
}
public DataTemplate IssueTemplate
{
get;
set;
}
}
I then declare the selector as a Resource in xaml assigning the two templates I want to use for Repository and Issues.
<selectors:IssueSummaryTemplateSelector x:Key="IssueSummarySelector"
IssueTemplate="{StaticResource IssueGridZoomedOutTemplate}"
RepositoryTemplate="{StaticResource IssueGridRepositoryZoomedOutTemplate}"/>
You can then use it on your GridView.
<GridView ItemTemplateSelector="{StaticResource IssueSummarySelector}" />

Another "The name 'itembutton' does not exist in the current context" (WP8, XAML)

I know that this question has been posted about a thousand times, but I did not find a solution that solved my problem.
I've got a LongListSelector with this ItemTemplate:
<DataTemplate x:Key="AddrBookItemTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Name}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Grid.Column="0" Grid.Row="0" />
<Button x:Name="itembutton" CommandParameter="{Binding ItemID}" Content="{Binding ButtonCaption}" Width="150" HorizontalAlignment="Right" Grid.Column="1" Grid.Row="0" Click="ItemButtonClick"/>
</Grid>
</DataTemplate>
So what happens is simply that I get this beautiful error message that I posted in the title. And I do not have a clue, why?!
private void ItemButtonClick(object sender, RoutedEventArgs e)
{
if (sender == this.itemButton) {
....
From the DataTemplate it looks like you're showing this button in a container like a ListBox or LongListSelector.
That means that there isn't an itembutton in this scope (presumably this is either UserControl or PhoneApplicationPage): there is in fact one for each of your ListBoxItems!
You'll have to find another way of finding your button.
Update:
You should find that the DataContext of your button matches the item of the list it is contained in. That's probably the easiest way of proceeding: however if all else fails you can use the Tag property as you suggest.
Like:
private void ItemButtonClick(object sender, RoutedEventArgs e)
{
var theButton = (Button) sender;
var myItem = (TypeOfAnObjectInMyList) theButton.DataContext;
doSomethingWithMyItem(myItem);

WinRT Combobox strange behaviour in template

I have a strange problem with a combobox in a WinRT application running under Win 8 Release Preview. Here is a code snippet :
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Name, Mode=TwoWay}" VerticalAlignment="Center"/>
<ComboBox Grid.Column="1" ItemsSource="{Binding Source={StaticResource evtTypeRes}}" SelectedValuePath="ID" DisplayMemberPath="Name" SelectedValue="{Binding EventTypeID, Mode=TwoWay}" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
The ListView is backed by ObservableCollection list and the items implement INotifyPropertyChanged. The problem is that when I change the value of a combobox in the screen the value is changed not only for the particular row, but every row (the setter for the property is called on every object form the collection). This is not the case with the text box. What could be the reason for this strange behavior ?
Thanks in advance
I found the purpose of the IsSynchronizedWithCurrentItem attribute : value False solved my problem.