Binding WPF menu items to WPF Tab Control Items collection - data-binding

I have a WPF Menu and a Tab control. I would like the list of menu items to be generated from the collection of TabItems on my tab control. I am binding my tab control to a collection to generate the TabItems. I have a TabItem style that uses a ContentPresenter to display the TabItem text in a TextBlock. When I bind the tab items to my menu the menu items are blank. I am using a Style Setter to set the Menu Item name, but I am not sure what property of the TabItem I would use to set the MenuItem text. Is there a workaround for my scenario? Is it possible to bind to the Header property of the tab item, when I do not know the number of tabs in advance? Below is a copy of my xaml declarations. Tab Control and items:
<DataTemplate x:Key="ClosableTabItemTemplate">
<DockPanel HorizontalAlignment="Stretch">
<Button
Command="{Binding Path=CloseWorkSpaceCommand}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Courier"
FontSize="9"
FontWeight="Bold"
Margin="10,1,0,0"
Padding="0"
VerticalContentAlignment="Bottom"
Width="16" Height="16"
Background="Red"
/>
<ContentPresenter HorizontalAlignment="Center"
Content="{Binding Path=DisplayName}">
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="10"
Background="#4C4C4C"/>
</DataTemplate>
My Menu and partial style listing
//I am not sure which value am I suppose to use, since I am not using header
<Menu Background="Transparent">
<MenuItem Style="{StaticResource TabMenuButtonStyle}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TabControl}}, Path=Items}"
ItemContainerStyle="{StaticResource TabMenuItem}">
</MenuItem>
</Menu>

Create the following style and bind Header property to display property in ViewModel
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding PropertyInViewModel}" />
</Style>

Related

Customize look of selected element

Is there any possibility to customize the look of the active/selected item in the indicator view? Using Data Template Selector or something?
Data template changes every single item.
My goal is to reach this effect:
Try using trigger when color changes of your view at that time increase view's width or you may use view's PropertyChanged event to detect changes in view. I had a similar kind of requirement today and below code worked for me.
<IndicatorView
x:Name="introductionIndicator"
Grid.Row="2"
HorizontalOptions="Center"
IndicatorColor="{StaticResource PrimaryLightGrayColor}"
SelectedIndicatorColor="{StaticResource PrimaryYellowColor}">
<IndicatorView.IndicatorTemplate>
<DataTemplate>
<BoxView
CornerRadius="10"
HeightRequest="6"
WidthRequest="20">
<BoxView.Triggers>
<Trigger TargetType="BoxView" Property="BackgroundColor" Value="{StaticResource PrimaryYellowColor}">
<Setter Property="WidthRequest" Value="30" />
</Trigger>
</BoxView.Triggers>
</BoxView>
</DataTemplate>
</IndicatorView.IndicatorTemplate>
</IndicatorView>
result:

Xamarin: Hide the button if condition is false

I am new to Xamarin Forms.
I want to know how to hide a button if a specific condition is false.
ex: I need to hide the submit button until the text field has a value.
(if the text field is null, cannot click the submit button)
this is my .xaml code
<Entry Grid.Row="0"
Margin="10,10,10,20"
x:Name="ground_area"
Grid.Column="1"
MaxLength="5"
HorizontalOptions="Center"
Placeholder="area"
FontSize="18"/>
<Button x:Name="avg_nut"
Text="Submit"
Grid.Row="5"
VerticalOptions="Center"
Margin="40,0,20,50"
Style="{StaticResource buttonStyle}"
Clicked="submit_click">
</Button>
Hey you can use the property of the button IsEnabled="{Binding booleanProperty}" and in your view model you can manipulate the value of this property depending in what you want
I recommended you to use View Model instead of Code Behind is not that hard learn how works MVVM (Model View ViewModel)
That's my advice.
I hope this can solve your problem
Use Trigger:
<Entry x:Name="ground_area"/>
<Button Text="Submit">
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding Text.Length, Source={Reference ground_area}, FallbackValue=0}" Value="0">
<Setter Property="IsVisible" Value="False"/>
</DataTrigger>
</Button.Triggers>
</Button>
Also, check IsEnabled property, it's better than hiding the control.

XamarinForm DataTemplate ListView Binding

I want to bind to a variable from inside a CustomControl referenced in a ListView DataTemplate
This is the situation:
I have a custom control containing a ListView with many DataTemplates to display the various ViewCell (it's basically a set of dynamic fields that must be displayed according to their "type").
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:s4gvForms="clr-namespace:S4GVMobile.Forms;assembly=S4GVMobile"
xmlns:s4gvControls="clr-namespace:S4GVMobile.Controls;assembly=S4GVMobile"
x:Class="S4GVMobile.Controls.AttributesList"
x:Name="S4GVAttributesListControl"
>
<ContentView.Resources>
<ResourceDictionary>
<!-- MANY DATA TEMPLATES HERE, ONLY ONE LEFT FOR REFERENCE -->
<DataTemplate x:Key="s4gvAttributePictureTemplate">
<ViewCell x:Name="s4gvAttributePictureViewCell">
<ViewCell.View>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Attribute.Key}" />
<s4gvControls:AttributePicture ParentID="{Binding Source={x:Reference s4gvAttributePictureViewCell}, Path=Parent.BindingContext.EntityID}" AttributeValue="{Binding .}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
<!-- ... -->
<!-- AGAIN, LONG LIST IN THE SELECTOR -->
<s4gvForms:AttributeDataTemplateSelector x:Key="s4gvAttributeDataTemplateSelector"
AttributePictureTemplate="{StaticResource s4gvAttributePictureTemplate}"
/>
</ResourceDictionary>
</ContentView.Resources>
<StackLayout>
<ListView x:Name="s4gvAttributes" HasUnevenRows="True" ItemTemplate="{StaticResource s4gvAttributeDataTemplateSelector}" ItemsSource="{Binding AttributeValues}" />
</StackLayout>
I'm passing to this custom control both the list of items (required by the ListView) and an additional EntityID.
<s4gv:AttributesList x:Name="s4gvSerialAttributes" AttributeValues="{Binding Serial.AttributeValues}" EntityID="{Binding Serial.SerialID}" VerticalOptions="FillAndExpand"/>
The custom control has the required bindable properties (AttibuteValues and EntityID) and relies on a dedicated ViewModel
I need to retrieve the EntityID from inside the DataTemplate (and put it in the ParentID property); this value is in the custom control's ViewModel but it is "outside" the ListView's DataTemplates. I've tried using the Parent.BindingContext (it works on Command and CommandParameter) but it seems that it can only go up to the ListView's context and not higher though I need to move one further step up to the CustomControl itself.
<s4gvControls:AttributePicture ParentID="{Binding Source={x:Reference s4gvAttributePictureViewCell}, Path=Parent.BindingContext.EntityID}" AttributeValue="{Binding .}" />
Any idea? I'm happy to restructure the whole thing if required.

Bind Pivot Header Template

I have a Pivot Control and three PivotItems added in Blend. Each PivotItem has a ViewModel as it's DataContext. Each ViewModel has a string property Title. I want to edit the Header Template, create a TextBlock and bind it's Text property to Title, but for some reason it doesn't work. I am sure it's because the DataContext of the Header Template is null, but I thought it would be inherited from the specific PivotItem. This is the xaml code for the Header Template.
<DataTemplate x:Key="HeaderTemplate">
<Grid Height="47" Width="354">
<TextBlock Margin="8,0,64,8" TextWrapping="Wrap" Text="{Binding Title}" d:LayoutOverrides="Width" FontSize="24" Foreground="Red"/>
</Grid>
</DataTemplate>
<controls:Pivot TitleTemplate="{StaticResource TitleTemplate}" HeaderTemplate="{StaticResource HeaderTemplate}">
<!--Pivot item one-->
<controls:PivotItem DataContext="{Binding Home, Mode=OneWay}">
<Grid>
<ListBox x:Name="listBox" ItemsPanel="{StaticResource HomeItemsPanel}" ItemTemplate="{StaticResource HomeItemTemplate}" ItemsSource="{Binding Tiles}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding OnSelectionChanged}" CommandParameter="{Binding SelectedIndex, ElementName=listBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
</Grid>
</controls:PivotItem>
<controls:PivotItem Margin="0,8,0,1">
<Grid/>
</controls:PivotItem>
<!--Pivot item two-->
<controls:PivotItem DataContext="{Binding More, Mode=OneWay}">
<Grid/>
</controls:PivotItem>
</controls:Pivot>
I want to create a more complex Header Template but for now I am testing if I can bind it, before proceeding on with something more than just a TextBlock. I know that I could use a List of ViewModels for the PivotControl.ItemSource, but then I would have to use a DataTemplateSelector and so on, and I don't want that.
So I guess my question is this, how can I make the HeaderTemplate inherit the DataContext of the according PivotItem?
Thank you.
That won't work. You should write HeaderTemplate the following way:
<controls:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</controls:Pivot.HeaderTemplate>
Textblock will receive the binding from the PivotItem's Header property. For each PivotItem, you bind its Header:
<controls:PivotItem Header="{Binding Title}">
<Grid/>
</controls:PivotItem>
The only problem remains in binding each pivot item to its own data context. You can do that manually for each item with the following code:
<controls:PivotItem DataContext="{Binding Item1}" Header="{Binding Title}">
<Grid/>
</controls:PivotItem>
<controls:PivotItem DataContext="{Binding Item2}" Header="{Binding Title}">
<Grid/>
</controls:PivotItem>

Bound combobox: text disappearing after sorting the source list of strings

Ive got an ObservableCollection<string> list, which is bound to a combobox. This combobox is in a datatemplate which is inside a 'DataGridTemplateColumn'.
When the datagrid is displayed (with all the rows), the column displaying this combobox works just fine. The user can select the items in the combobox, and when it's selected, the string is bound to the cell. (Just for your info: the datagrid is bound to another ObservableCollection so the cell text gets updated in that list - but i don't think it's relevant to my problem).
This is all good but a problem arises when i go to 'add' another item in the ObservableCollection<string> list that the combo box is bound to, and perform a sort. The text disappears in the 'textbox' part of some of the previously modified comboboxes. If i do not sort the list, (just add a new value) everything is fine.
I think what is happenning is that the binding gets screwed up when i re-sort the list. Because the list has 'changed', the order of the strings in the list are now different, so the binding doesn't know what to display.
How can i get this to work? The previously selected comboboxes's text disappears when i re-sort the ObservableCollection<string> list.
My <DataGridTemplateColumn> containing the combo box is:
<WpfToolkit:DataGridTemplateColumn
Header="Category" Width="1*"
CellTemplate="{StaticResource ComboBoxCellDataTemplate}"
CellEditingTemplate="{StaticResource ComboBoxCellEditingTemplate}"/>
...and the related DataTemplates are:
<DataTemplate x:Key="ComboBoxCellDataTemplate">
<Label x:Name="lblCombo" Content="{Binding Category}" Style="{StaticResource BaseLabelCellStyle}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Categories, Mode=TwoWay}" Value="Both">
<Setter TargetName="lblCombo" Property="IsEnabled" Value="False" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="ComboBoxCellEditingTemplate">
<!-- min=60, max=600 also, add in a 'specific' scalar value -->
<ComboBox
x:Name="comboBox"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Categories, Mode=TwoWay}"
SelectedItem="{Binding Category}" LostFocus="comboBox_LostFocus" IsEditable="True" PreviewKeyDown="comboBox_PreviewKeyDown" MaxDropDownHeight="100" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter TargetName="comboBox" Property="IsEnabled" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Categories, Mode=TwoWay}" Value="Both">
<Setter TargetName="comboBox" Property="IsEnabled" Value="True" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Note that the majority of this code is by Samuel Moura at http://sweux.com/blogs/smoura/index.php/tag/datagridcolumn/
Hey I think I have a solution for you. Just add the following line to your Datagrid definition
SelectionUnit="Cell"
I dunno how, it worked for me:) Just give it a try and let me know if it helps.

Resources