I'm developing a Windows Phone 8 and I have a single selection Listbox and this DataTemplate:
<DataTemplate x:Key="LocalizationItemTemplate">
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="8" Background="#FF003847" Height="80">
<Grid x:Name="contentGrid" Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*"/>
<ColumnDefinition Width="90*"/>
</Grid.ColumnDefinitions>
<CheckBox x:Name="selectedCheck" Content="CheckBox" HorizontalAlignment="Center" Height="20" Margin="0" VerticalAlignment="Center" Width="20"/>
<TextBlock x:Name="locationName" TextWrapping="Wrap" Text="{Binding Name}" Margin="10,34,0,34" VerticalAlignment="Center" FontSize="24" Grid.ColumnSpan="2" Height="0"/>
</Grid>
</Border>
</DataTemplate>
How can I access the selectedCheck CheckBox programmatically?
private T FindElementInVisualTree<T>(DependencyObject parentElement, string name) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is FrameworkElement && (child as FrameworkElement).Name.Equals(name))
{
return (T)child;
}
else
{
var result = FindElementInVisualTree<T>(child, name);
if (result != null)
return result;
}
}
return null;
}
Usage:
ListBoxItem item = list.ItemContainerGenerator.ContainerFromItem(list.SelectedItem) as ListBoxItem;
CheckBox check = FindElementInVisualTree<CheckBox>(item, "selectedCheck");
But, I think you need to bind IsChecked property on selectedCheck object to manipulate on it
<CheckBox x:Name="selectedCheck" IsChecked={Binding Checked, Mode=TwoWay} ...
Related
I have a Listview that is creating dynamically data and a switch in every row, what i want is when i toggle a switch the others untoggle.
Is this possible to do?
Example:
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="{Binding article_description}"
FontAttributes="Bold" FontSize="13" Margin="10,5,0,-6" Grid.Row="0" LineBreakMode="NoWrap"/>
<Label Text="{Binding dish_name}"
FontSize="13" Margin="10,0,0,2" Grid.Row="1" Grid.Column="0"/>
<Label Grid.Row="0" Grid.Column="0" x:Name="LabelReserved" Text="{Binding reserved}" IsVisible="false" LineBreakMode="NoWrap"/>
<Switch Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" HorizontalOptions="Start" VerticalOptions="Center" IsEnabled="False" Toggled="SwitchMenu_OnToggled" >
<Switch.Triggers>
<DataTrigger TargetType="Switch" Binding="{Binding Source={x:Reference LabelReserved},
Path=Text.Length}" Value="7">
<Setter Property="IsToggled" Value="true" />
</DataTrigger>
</Switch.Triggers>
</Switch>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
Note: This solution was a bit of an experiment on my part - so I would recommend that, if you decide to implement this, use it with caution.
The intent here is to extend Switch to be able to act as a grouped radio button.
First step would be to create a IsToggled or IsChecked or similar property in the item that acts as BindingContext to each list-item. You can implement an interface like:
public interface IToggableItem
{
string GroupName { get; } //not mandatory, only added to support grouped lists
bool IsChecked { get; set; }
}
Second step would be extend Switch to be aware of items-list. We can do that by adding a GroupContext bindable property - which basically represents the parent list-view's ItemsSource.
When a switch is toggled, it iterates through the items-list to set the property as false on other items.
For example:
public class GroupedSwitch : CustomSwitch
{
public static readonly BindableProperty IsGroupingEnabledProperty =
BindableProperty.Create(
"IsGroupingEnabled", typeof(bool), typeof(GroupedSwitch),
defaultValue: default(bool));
public bool IsGroupingEnabled
{
get { return (bool)GetValue(IsGroupingEnabledProperty); }
set { SetValue(IsGroupingEnabledProperty, value); }
}
public static readonly BindableProperty GroupContextProperty =
BindableProperty.Create(
"GroupContext", typeof(IEnumerable), typeof(GroupedSwitch),
defaultValue: default(IEnumerable));
public IEnumerable GroupContext
{
get { return (IEnumerable)GetValue(GroupContextProperty); }
set { SetValue(GroupContextProperty, value); }
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName != nameof(IsToggled))
return;
if (IsToggled != true || GroupContext == null)
return;
var currentItem = BindingContext as IToggableItem;
if (currentItem == null)
return;
if (IsGroupingEnabled)
{
var groupList = GroupContext as IEnumerable<IGrouping<string, IToggableItem>>;
var currentGroup = groupList.FirstOrDefault(x => x.Key == currentItem.GroupName);
if (currentGroup != null)
foreach (var item in currentGroup)
{
if (item != currentItem)
item.IsChecked = false;
}
}
else
{
var simpleList = GroupContext as IEnumerable<IToggableItem>;
if (simpleList != null)
foreach (var item in simpleList)
{
if (item != currentItem)
item.IsChecked = false;
}
}
}
}
Third step would be bind GroupContext property to parent ListView's items source. For example:
<ListView x:Name="ParentListView" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" VerticalOptions="Center">
<Label Text="{Binding Name}" />
<local:GroupedSwitch
IsToggled="{Binding IsChecked}"
GroupContext="{Binding ItemsSource, Source={x:Reference ParentListView}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Or,
<ListView x:Name="_parentList" IsGroupingEnabled="true" >
<ListView.GroupHeaderTemplate>
<DataTemplate>
<TextCell Text="{Binding Key}" />
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" />
<local:GroupedSwitch
ToggledStateFromCode="{Binding IsSwitchOn}"
IsGroupingEnabled="true"
GroupContext="{Binding ItemsSource, Source={x:Reference _parentList}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
EDIT 1: Updated code to add support for grouped list.
You should use triggers on each switch. Check this out:
https://www.tutorialspoint.com/xaml/xaml_triggers.htm
I have 3 panorama items defined in xaml. I am setting visibility=collapsed for 2nd and 3rd panorama item .
In code behind I am setting visibility=Visible for 2nd and 3rd panorama item after certain condition occured. When I debug code,I see visibility=Visible is executed but then also these 2 items are in collapsed state only.What may be the reason?
<phone:PanoramaItem Header="enterprise" Visibility="Collapsed"
HeaderTemplate="{StaticResource PanoramaItemHeaderTemplate}"
Name="enterpriseApps">
<Grid Margin="16,0,0,0">
<!--<StackPanel>-->
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--<Button x:Name="btnUpdate" Click="btnUpdateAll_Click" Content="Update Apps"
FontSize="30" HorizontalAlignment="Left" Visibility="Collapsed"
Grid.Row="0" Margin="-10 -10 0 0"/>-->
<ListBox x:Name="EnterpriseApplist" Grid.Row="1"
ItemsSource="{Binding EnterpriseAppList}"
ItemTemplate="{StaticResource AvailableAppDataTemplate}"
SelectionChanged="InstalledCompanyAppList_SelectionChanged"/>
<TextBlock Name="txtEnterpriseapps" Visibility="Collapsed" Grid.Row="1"/>
</Grid>
</phone:PanoramaItem>
<phone:PanoramaItem Header="uat" Visibility="Collapsed"
HeaderTemplate="{StaticResource PanoramaItemHeaderTemplate}"
Name="uatApps">
<Grid Margin="16,0,0,0">
<!--<StackPanel>-->
<ListBox x:Name="UATAppList"
ItemsSource="{Binding UATAppList}"
ItemTemplate="{StaticResource AvailableAppDataTemplate}"
SelectionChanged="InstalledCompanyAppList_SelectionChanged">
</ListBox>
<TextBlock Name="txtUatapps" Visibility="Collapsed"/>
<!--</StackPanel>-->
</Grid>
</phone:PanoramaItem>
<phone:PanoramaItem Header="demo" Name="demoApps"
HeaderTemplate="{StaticResource PanoramaItemHeaderTemplate}">
<Grid Margin="16,0,0,0">
<!--<StackPanel>-->
<ListBox x:Name="DemoAppList" Visibility="Collapsed"
ItemsSource="{Binding DemoAppList}"
ItemTemplate="{StaticResource AvailableAppDataTemplate}"
SelectionChanged="InstalledCompanyAppList_SelectionChanged"/>
<TextBlock Name="txtDemoapps" Visibility="Collapsed"/>
<!--</StackPanel>-->
</Grid>
</phone:PanoramaItem>
</phone:Panorama>
In code behind, I am writing as
if (App.ViewModel.EnterpriseAppList.Count == 0)
{
enterpriseApps.Visibility = Visibility.Visible;
if (App.ViewModel.UATAppList.Count == 0)
{
uatApps.Visibility = Visibility.Visible;
}
if (App.ViewModel.DemoAppList.Count == 0)
{
demoApps.Visibility = Visibility.Visible;
}
The Panorama measured it's size and arrange when control Initialize. You can change your method like this(the name of Panorama is panorama):
if (App.ViewModel.EnterpriseAppList.Count == 0)
{
int index1 = panorama.Items.IndexOf(enterpriseApps);
panorama.Items.RemoveAt(index1);
panorama.Items.Insert(index1, enterpriseApps);
enterpriseApps.Visibility = Visibility.Visible;
if (App.ViewModel.UATAppList.Count == 0)
{
int index2 = panorama.Items.IndexOf(uatApps);
panorama.Items.RemoveAt(index2);
panorama.Items.Insert(index2, uatApps);
uatApps.Visibility = Visibility.Visible;
}
if (App.ViewModel.DemoAppList.Count == 0)
{
int index3 = panorama.Items.IndexOf(demoApps);
panorama.Items.RemoveAt(index3);
panorama.Items.Insert(index3, demoApps);
demoApps.Visibility = Visibility.Visible;
}
}
I have this list of objects that needs to be separated into two columns. Then I use this template for every object of the list:
<DataTemplate x:Key="UnderlyingRealTimeExchangeRatesLongListSelector">
<Grid Background="{Binding PriceChanged, Converter={StaticResource PriceChangedToBackgroundConverter}}"
Margin="0,2.5,5,2.5" Tap="RealTimeElement_Tapped">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="txtUnderlyingName" Text="{Binding Name}" Foreground="White"
Style="{StaticResource NormalFontStyle}" HorizontalAlignment="Left"
Margin="5" FontSize="25" Padding="10" TextWrapping="Wrap"/>
<Image Grid.Column="1"
Source="{Binding Path= Image, Converter={StaticResource ImageToFlagConverter}}"
Height="30"></Image>
<TextBlock x:Name="txtUnderlyingPrice" Text="{Binding Price, StringFormat='0:N2'}"
Grid.Row="1" Grid.ColumnSpan="2" Foreground="#FFD300"
Style="{StaticResource LightFontStyle}" FontSize="40"
HorizontalAlignment="Right" VerticalAlignment="Center" Padding="10"/>
</Grid>
</DataTemplate>
<phone:LongListSelector x:Name="llsRealTimeCurrencies1" Grid.Column="0" Margin="0,15,0,32"
ItemTemplate="{StaticResource UnderlyingRealTimeExchangeRatesLongListSelector}"
Visibility="Collapsed"/>
<phone:LongListSelector x:Name="llsRealTimeCurrencies2" Grid.Column="1" Margin="0,15,0,32"
ItemTemplate="{StaticResource UnderlyingRealTimeExchangeRatesLongListSelector}"
Visibility="Collapsed"/>
Here is the converter:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//Returns The flag needed
if (value != null)
{
string image = value.ToString().ToLower();
string flag = "Assets\\flags\\" + image + "_flag.png";
return Path.GetFullPath(flag);
}
else
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
Here is the code behind the previous part:
private void SetAndShowCorrectRealTimeList<T>(LongListSelector[] list, IList<T> collection)
{
//and make sure the grid holding the lists is visible
grdRealtimeLists.Visibility = System.Windows.Visibility.Visible;
List<LongListSelector> lists =
new List<LongListSelector>()
{
llsRealTimeCommodities1,
llsRealTimeCommodities2,
llsRealTimeCurrencies1,
llsRealTimeCurrencies2,
llsRealTimeIndecies1,
llsRealTimeIndecies2,
llsRealTimeWatchList1,
llsRealTimeWatchList2
};
foreach (var item in lists.Except(list))
{
item.Visibility = System.Windows.Visibility.Collapsed;
}
for (int i = 0; i < list.Length; i++)
{
list[i].Visibility = Visibility.Visible;
List<T> result = collection.Where((item, index) => index % 2 == i).ToList();
list[i].ItemsSource = result;
}
}
The funny part is that it displays the images on the left column, but doesn't on the right. As a matter of fact, it doesn't even gets in the converter for the second column. Thank you!!!
Bind image source like this.
<Image Grid.Column="1" Height="30">
<Image.Source>
<BitmapImage UriSource="{Binding Path=Image, Converter={StaticResource ImageToFlagConverter}}" />
</Image.Source>
</Image>
Check to see if your code has all the right elements -
llsRealTimeCurrencies1, llsRealTimeCurrencies2. Those are the lists that we use to bind the template to:
private async void Currencies_Tapped(object sender, System.Windows.Input.GestureEventArgs e)
{
mainViewModel.RealTimeViewModel.IsWatchListSettingsVisible = false;
ApplyForegroundFont(sender, realtimeTabs);
FrameworkElement parent = (FrameworkElement)((TextBlock)sender).Parent;
ApplyBackgroundColor(parent, realtimeTabsGrid);
if (!mainViewModel.RealTimeViewModel.CurrencyExchangeRates.Any())
{
if (mainViewModel.RealTimeViewModel.Realtime != null &&
mainViewModel.RealTimeViewModel.Realtime.Underlyings != null &&
mainViewModel.RealTimeViewModel.Realtime.Underlyings.Any())
{
mainViewModel.RealTimeViewModel.InitializeCurrencies();
}
else
{
mainViewModel.RealTimeViewModel.LoadProductsOrUnderlyingsFromDb<Currency>(mainViewModel.RealTimeViewModel.CurrencyExchangeRates);
}
}
if (!App.Settings.Contains(currenciesWereSaved))
{
App.Settings.Add(currenciesWereSaved, "");
App.Settings.Save();
await Task.Factory.StartNew(() =>
mainViewModel.RealTimeViewModel.SaveUnderlyingsAndProducts<Currency>(mainViewModel.RealTimeViewModel.CurrencyExchangeRates));
}
SetAndShowCorrectRealTimeList(new LongListSelector[] { llsRealTimeCurrencies1, llsRealTimeCurrencies2 }, mainViewModel.RealTimeViewModel.CurrencyExchangeRates);
GoogleAnalytics.EasyTracker.GetTracker().SendEvent("Real time", "click", "Currencies", 0);
MyBindableBase.LightstreamerClientInstance.Subscribe(mainViewModel.RealTimeViewModel.CurrencyExchangeRates.Cast<IRealtimeProperty>().ToList());
}
I have to insert elements to a itemscontrol at top of the list. While inserting an element to the list at 0th position, list is scrolling to that item. But i don't want to scroll the item to the top of the list, when an element is inserted. Any ideas for making this possible.
Code of xaml :
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
<Button Height="72" Content="Add More" Click="Button_Click"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<ScrollViewer x:Name="MainScrollViewer" Grid.Row="1" Margin="12,0,12,0" >
<Grid x:Name="ContentPanel" >
<ItemsControl x:Name="MainItemsControl" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Margin="10" Text="{Binding Item}" FontSize="28" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ScrollViewer>
</Grid>
My class Code:
public partial class MainPage : PhoneApplicationPage
{
private ObservableCollection<Model> mainList;
public ObservableCollection<Model> MainList
{
get { return mainList; }
set
{
mainList = value;
}
}
public MainPage()
{
InitializeComponent();
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
MainList = new ObservableCollection<Model>();
AddItems(15);
ContentPanel.DataContext = MainList;
}
void AddItems(int numberIfItemsToAdd)
{
Debug.WriteLine("AddItems()");
int start = MainList.Count;
int end = start + numberIfItemsToAdd;
for (int i = start; i < end; i++)
{
Model model = new Model();
model.Item="Item" + i;
MainList.Insert(0, model);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
AddItems(1);
}
}
Thank you in advance.
I need to get the values from Listbox selected items. Note that, the data templates are in data bound. here is the xaml:
<ListBox Name="AppointmentResultsData" ItemsSource="{Binding}" Height="650" Width="480" Margin="24,0,0,0" Foreground="#CBF7FA" SelectionChanged="AppointmentResultsData_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Path=Subject, Mode=TwoWay}" TextWrapping="Wrap" FontSize="30" Grid.Column="0" Grid.Row="1"/>
<Grid Grid.Column="0" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Path=Account.Name}" Grid.Column="0" Grid.Row="1" FontSize="28"/>
<TextBlock Text="Start : " Grid.Column="0" FontSize="22" Grid.Row="2"/>
<TextBlock Text="{Binding Path=StartTime}" FontSize="22" Grid.Column="1" Grid.Row="2"/>
<TextBlock Text="End : " Grid.Column="0" Grid.Row="3" FontSize="22"/>
<TextBlock Text="{Binding Path=EndTime}" Grid.Column="1" FontSize="22" Grid.Row="3"/>
<TextBlock Text="Location : " Grid.Column="0" Grid.Row="4" FontSize="22"/>
<TextBlock Text="{Binding Path=Location}" Grid.Column="1" FontSize="22" Grid.Row="4"/>
<TextBlock Text="Status : " Grid.Column="0" FontSize="22" Grid.Row="5"/>
<TextBlock Text="{Binding Path=Status}" Grid.Column="1" FontSize="22" Grid.Row="5"/>
</Grid>
<TextBlock Text=" "/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I need values of the textboxes in selection changed event.I have tried like this...
private void AppointmentResultsData_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//SelectedEvent seleted = AppointmentResultsData.SelectedItem as SelectedEvent;
if (AppointmentResultsData.SelectedIndex == -1)
return;
ListBoxItem currentSelectedListBoxItem = this.AppointmentResultsData.ItemContainerGenerator.ContainerFromIndex(AppointmentResultsData.SelectedIndex) as ListBoxItem;
if (currentSelectedListBoxItem == null)
return;
// Iterate whole listbox tree and search for this items
TextBox nameBox = helperClass.FindDescendant<TextBox>(currentSelectedListBoxItem);
TextBlock nameBlock = helperClass.FindDescendant<TextBlock>(currentSelectedListBoxItem);
MessageBox.Show(nameBlock.Text + " " + nameBox.Text);
}
But it didn't work !
Solved it !
private void AppointmentResultsData_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBoxItem = AppointmentResultsData.ItemContainerGenerator.ContainerFromIndex(AppointmentResultsData.SelectedIndex) as ListBoxItem;
var txtBlk = FindVisualChildByType<TextBlock>(listBoxItem, "txtLocation");
MessageBox.Show(txtBlk.Text);
}
T FindVisualChildByType<T>(DependencyObject element, String name) where T : class
{
if (element is T && (element as FrameworkElement).Name == name)
return element as T;
int childcount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childcount; i++)
{
T childElement = FindVisualChildByType<T>(VisualTreeHelper.GetChild(element, i), name);
if (childElement != null)
return childElement;
}
return null;
}
Suppose you have a list of class(MyClass) objects which you have databinded to listbox
Add a handler gesturelistener tap to the datatemplate
In the handler do this:
private void ItemClickedEventHandler(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
{
MyClass clickedMyclass = (MyClass)((System.Windows.Controls.Grid)sender).DataContext;
}
you have the object of the current selected item and you can access all the class variables. eg(StartTime etc.)
Well you are casting it to the wrong type, this should work :
private void AppointmentResultsData_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBoxItem = AppointmentResultsData.SelectedItem as ListBoxItem;
TextBox nameBox = listBoxItem .FindName("nameYourTextBox") as TextBox;
TextBlock nameBlock = dd.FindName("nameYourTextBlock") as TextBlock;
MessageBox.Show(nameBlock.Text + " " + nameBox.Text);
}
of course you need to add the Name to your TextBox and TextBlock
<TextBlock x:Name="nameYourTextBlock Text="{Binding Path=Account.Name}" Grid.Column="0" Grid.Row="1" FontSize="28"/>
Plus I don't see any TextBox in your XAML.