Updating control value in listview through messagecenter - xaml

I want to update value in entry control inside ListView from another Pop up page using messageCenter approach. It is not working as expected as it is updating multiple items in list view.
My base page (PageA) has ListView
<ListView
x:Name="workList"
Grid.Row="2"
SeparatorColor="{DynamicResource AccentColor}"
ItemsSource="{ Binding WorkItems}"
Margin="5"
CachingStrategy="RecycleElement"
RowHeight="440"
SeparatorVisibility="Default"
SelectionMode="None"
HasUnevenRows="False">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<local:LoadItemPutawayTemplate />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
LoadItemPutAwayTemplate is my content view for ListView as below
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
mc:Ignorable="d"
x:Class="Sanipex.LoadItemPutawayTemplate">
<Grid
RowSpacing="0"
Padding="0"
Margin="0,10,0,0"
>
<Grid.RowDefinitions>
<RowDefinition
Height="*" />
<RowDefinition
Height="auto" />
</Grid.RowDefinitions>
<Entry
x:Name="OverrideLoc"
Grid.Row="0"
TextColor="Black"
WidthRequest="110"
Text="{Binding toLocation}"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"
FontAttributes="Bold"
PlaceholderColor="Black"
FontSize="20"/>
<Button
Grid.Row="1"
HorizontalOptions="Center"
x:Name="LocationBtn"
Text="Override Loc"
BackgroundColor="#25D366"
TextColor="{DynamicResource AccentColor}"
BorderColor="#25D366"
Style="{ StaticResource CircleActionButtonFlatStyle }"
Clicked="LocationBtn_Clicked"/>
</Grid>
On LocationBtn_Clicked i am calling the popup
private async void LocationBtn_Clicked(object sender, EventArgs e)
{
var AvailableLocationPopUp = new AvailableLocationsPopUp();
await PopupNavigation.Instance.PushAsync(AvailableLocationPopUp);
}
Below is my Pop up page (AvailableLocationsPopUp) button click method from where I am sending the message
private void PutawayBtn_Clicked(object sender, EventArgs e)
{
MessagingCenter.Send<AvailableLocationsPopUp, string>(this,
"Location", "XYZ");
}
As i have entry control in my contentView of LoadItemPutAwayTemplate, I am subscribing message in its constructor as below to update the value of entry control
public LoadItemPutawayTemplate()
{
InitializeComponent();
MessagingCenter.Subscribe<AvailableLocationsPopUp, string>(this,
"Location", async (sender, arg) =>
{
this.OverrideLoc.Text = arg;
}
}
But this is updating values to all ListView items which are showing on UI. I just want to update the value of item from where the pop up was initiated.

When you subscribe to the message in the template, then all the ListView items will receive the message.
I have created a simple demo to simulate your app,the main code is:
LocationModel.cs
public class LocationModel: INotifyPropertyChanged
{
string toLocation;
public string ToLocation {
set { SetProperty(ref toLocation, value); }
get { return toLocation; }
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
LoadItemPutawayTemplate.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoadItemPutawayTemplate : ContentView
{
public LoadItemPutawayTemplate()
{
InitializeComponent();
}
private void LocationBtn_Clicked(object sender, EventArgs e)
{
LocationModel stringInThisCell = (LocationModel)((Button)sender).BindingContext;
System.Diagnostics.Debug.WriteLine("send msg current update = " + stringInThisCell.ToLocation);
string source = stringInThisCell.ToLocation;
string result = "XYZ";
string[] values = { source, result };
MessagingCenter.Send<LoadItemPutawayTemplate, string[]>(this, "Location", values);
}
}
LoadItemPutawayTemplate.xaml
<ContentView.Content>
<Grid
RowSpacing="0"
Padding="0"
Margin="0,2,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Entry
x:Name="OverrideLoc"
Grid.Column="0"
TextColor="Black"
WidthRequest="110"
Text="{Binding ToLocation}"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"
FontAttributes="Bold"
PlaceholderColor="Black"
FontSize="20"/>
<Button
Grid.Column="1"
HorizontalOptions="Center"
x:Name="LocationBtn"
Text="Override Loc"
BackgroundColor="#25D366"
TextColor="{DynamicResource AccentColor}"
BorderColor="#25D366"
Clicked="LocationBtn_Clicked"/>
</Grid>
</ContentView.Content>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public List<LocationModel> WorkItems { get; set; }
public MainPage()
{
InitializeComponent();
WorkItems = new List<LocationModel>();
WorkItems.Add(new LocationModel { ToLocation = "Tomato"});
WorkItems.Add(new LocationModel { ToLocation = "Zucchini" });
WorkItems.Add(new LocationModel { ToLocation = "Tomato2"});
WorkItems.Add(new LocationModel { ToLocation = "Romaine2" });
WorkItems.Add(new LocationModel { ToLocation = "Zucchin2"});
MessagingCenter.Subscribe<LoadItemPutawayTemplate, string[]>(this,
"Location", async (sender, values) =>
{
string source = values[0];
string result = values[1];
for (int i = 0; i < WorkItems.Count; i++)
{
LocationModel model = WorkItems[i];
if (source.Equals(model.ToLocation)) {
model.ToLocation = result;
break;
}
}
});
workList.ItemsSource = WorkItems;
}
}
Note:
Since I don't know the code of AvailableLocationsPopUp, so I send the message when I click the Button.
To simply the UI,I modified the UI a little in some places(xaml).
You can get the full demo here.
The result is :

Related

How to use vertical Scroll view to show binding data

I want a structure like this :
Click this to see the desired output
But with my code i'm getting this : Click this to see the output
Here is my xaml code :
<ScrollView Orientation="Horizontal">
<StackLayout Orientation="Horizontal" VerticalOptions="Start">
<Grid x:Name="ImagesListViews" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
<local:BindableStackLayout x:Name="featuredEventsList">
<local:BindableStackLayout.ItemDataTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical" Padding="0" Margin="-5,0,5,0" HorizontalOptions="Center" >
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" />
</StackLayout.GestureRecognizers>
<Image Source="{Binding ImageThumbURL}" Margin="0,0,0,0" WidthRequest="140" />
<Label Margin="0" Text="{Binding TitleInPrimaryLang}" FontSize="12" TextColor="Black" LineBreakMode="TailTruncation" WidthRequest="100"/>
</StackLayout>
</DataTemplate>
</local:BindableStackLayout.ItemDataTemplate>
</local:BindableStackLayout>
</StackLayout>
</ScrollView>
Any help would be highly appreciated. Thank you
you have to make customize control for this. Please go through this and let me know if any query.
1) Extend Scroll view with Customized template.
public class HorizontalListview : ScrollView
{
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(HorizontalListview), default(IEnumerable));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(HorizontalListview), default(DataTemplate));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public event EventHandler<ItemTappedEventArgs> ItemSelected;
public static readonly BindableProperty SelectedCommandProperty =
BindableProperty.Create("SelectedCommand", typeof(ICommand), typeof(HorizontalListview), null);
public ICommand SelectedCommand
{
get { return (ICommand)GetValue(SelectedCommandProperty); }
set { SetValue(SelectedCommandProperty, value); }
}
public static readonly BindableProperty SelectedCommandParameterProperty =
BindableProperty.Create("SelectedCommandParameter", typeof(object), typeof(HorizontalListview), null);
public object SelectedCommandParameter
{
get { return GetValue(SelectedCommandParameterProperty); }
set { SetValue(SelectedCommandParameterProperty, value); }
}
public void Render()
{
if (ItemTemplate == null || ItemsSource == null)
return;
var layout = new StackLayout();
layout.Spacing = 0;
layout.Orientation = Orientation == ScrollOrientation.Vertical ? StackOrientation.Vertical : StackOrientation.Horizontal;
foreach (var item in ItemsSource)
{
var command = SelectedCommand ?? new Command((obj) =>
{
var args = new ItemTappedEventArgs(ItemsSource, item);
ItemSelected?.Invoke(this, args);
});
var commandParameter = SelectedCommandParameter ?? item;
var viewCell = ItemTemplate.CreateContent() as ViewCell;
viewCell.View.BindingContext = item;
viewCell.View.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = command,
CommandParameter = commandParameter,
NumberOfTapsRequired = 1
});
layout.Children.Add(viewCell.View);
}
Content = layout;
}
}
2) Add Namespace top to your page.
xmlns:control="clr-namespace:**Projectname**.CustomControls"
3) Use Control,
<control:HorizontalListview Orientation="Horizontal">
<control:HorizontalListview.ItemTemplate>
<DataTemplate>
<ViewCell>
<!....Your Design.....>
</ViewCell>
</DataTemplate>
</control:HorizontalListview.ItemTemplate>
</control:HorizontalListview>
4) Bind your data.
**YourControlName**.ItemsSource = lLstPhotoGallery; // Your List
**YourControlName**.Render();

TextBox not triggering PropertyChanged event

I am struggling to figure out why a propertychanged event or lostfocus event is not being triggered when I enter text into a textbox and navigate away from the control.
I am building a Universal Windows Platform app.
I know I'm doing something retarded but I just don't see it.
Any suggestions?
XAML:
<Page
x:Class="xxx.Client.FormPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:xxx.xxx.ViewModels"
Background="LightBlue"
mc:Ignorable="d">
<Page.Resources>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="TextAlignment" Value="Center" />
</Style>
</Page.Resources>
<Page.DataContext>
<viewModels:FormViewModel />
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding InstructorNameInput}" Style="{StaticResource TextBoxStyle}"
PlaceholderText="Instructor Name" />
<TextBox Grid.Row="1" Text="{Binding InstructorIdInput}" Style="{StaticResource TextBoxStyle}"
PlaceholderText="Instructor Id" />
<TextBox Grid.Row="2" Text="{Binding CourseInput}" Style="{StaticResource TextBoxStyle}"
PlaceholderText="Course" />
<Button Grid.Row="4" Content="Accept" Command="{Binding Submit}"
HorizontalAlignment="Stretch"
Foreground="Black" />
</Grid>
</Page>
ViewModel:
public partial class FormViewModel : ViewModelBase
{
public FormViewModel()
{
InstructorName = new RequiredField("Instructor's Name:");
InstructorId = new RequiredField("Instructor's Employee#:");
Course = new RequiredField("Course:");
var courses = CourseFactory.Produce();
var administration = new Administration(courses, null);
Submit = new DelegateCommand(_=> OnSubmit(), CanSubmit);
}
string _instructorNameInput = null;
public string InstructorNameInput
{
get { return _instructorNameInput; }
set
{
if (_instructorNameInput != value)
{
_instructorNameInput = value;
OnNotifyPropertyChanged();
}
}
}
string instructorIdInput = null;
public string InstructorIdInput
{
get { return instructorIdInput; }
set
{
if (instructorIdInput != value)
{
instructorIdInput = value;
OnNotifyPropertyChanged();
}
}
}
string courseInput = null;
public string CourseInput
{
get { return courseInput; }
set
{
if (courseInput != value)
{
courseInput = value;
OnNotifyPropertyChanged();
}
}
}
public RequiredField InstructorName { get; }
public RequiredField InstructorId { get; }
public RequiredField Course { get; }
public ObservableCollection<RequiredField> RequiredFields { get; } = new ObservableCollection<RequiredField>();
}
ViewModel.internal:
public partial class FormViewModel
{
static void OnSubmit()
{
var adminsistration = new Administration();
}
bool CanSubmit(object obj) => !GetUnsatisfied().Any();
IEnumerable<RequiredField> GetUnsatisfied()
{
if (string.IsNullOrEmpty(InstructorIdInput))
{
RequiredFields.Add(InstructorId);
}
else
{
var result = 0;
var isNumeric = int.TryParse(InstructorIdInput, out result);
if (!isNumeric)
{
RequiredFields.Add(new RequiredField(InstructorId.About, "Instructor ID must be numeric"));
}
}
if (string.IsNullOrEmpty(InstructorNameInput))
{
RequiredFields.Add(InstructorName);
}
if (string.IsNullOrEmpty(CourseInput))
{
RequiredFields.Add(Course);
}
return RequiredFields;
}
}
The default binding mode for UWP as MSDN says:
The default is OneWay.
Set it to TwoWay:
<TextBox Grid.Row="0" Text="{Binding InstructorNameInput, Mode=TwoWay}" .../>

PropertyChangedEventHandler null

I have 2 ObservableCollection<T>s and each of the them has their Cards.
public class Card: INotifyPropertyChanged
{
private string _CardTitle;
public string CardTitle
{
get { return _CardTitle; }
set
{
_CardTitle = value;
OnPropertyChanged("CardTitle");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Now when a CardTitle of the card is changed it is reflected on the UI thanks to PropertyChangedEventHandler.
But when I move a card from one collection to another and then change the CardTitle, I get handler as null thus, PropertyChangedEventHandler is not fired and I cannot see the change in UI.
I have scratched my head enough but cannot figure out why. If anyone has any idea then please help me before I get in trouble.
What do you mean by "move"? You remove from first collection, and add to second yes? Does your datacontext implement INotifyPropertyChanged?
I tried to reproduce your code, but i didn't have any problem. Look, maybe you find some hints:
MainPage.xaml.cs
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
this.DataContext = this;
}
private ObservableCollection<Card> _first;
public ObservableCollection<Card> First
{
get { return _first; }
set
{
_first = value;
OnPropertyChanged("First");
}
}
private ObservableCollection<Card> _second;
public ObservableCollection<Card> Second
{
get { return _second; }
set
{
_second = value;
OnPropertyChanged("Second");
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
//Init collections
}
private void ChangeItemName(object sender, RoutedEventArgs e)
{
var card = (sender as Button).DataContext as Card;
card.CardTitle = ":)";
}
private void ChangeCollection(object sender, RoutedEventArgs e)
{
var card = (sender as Button).DataContext as Card;
First.Remove(card);
Second.Add(card);
card.CardTitle = "xD";
}
MainPage.xaml
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding First}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding CardTitle}" />
<Button Content="Change name" Click="ChangeItemName" />
<Button Content="Change collection" Click="ChangeCollection" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView ItemsSource="{Binding Second}" Grid.Column="1">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding CardTitle}" />
<Button Content="Change name" Click="ChangeItemName" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</StackPanel>

LonglistSelector binding to Image Collection does not display

I have a xaml page. xaml page wants to show two TextBlocks and one LonglistSelector.
the two TextBlocks datasource come from an object(SpecifiedArticle);the LonglistSelctor itemsSource comes from Collection(ImageUriCollection).
when the page launch, the images cannot be displayed.
two TextBlocks data show well
LonglistSelctor does not show images; but i am sure ImageUriCollection's data can be get from the xaml because i tested in a image control and it works
<Image Source="{Binding ImageUriCollection[0].ImageSource}" Width="108" Height="108" Stretch="UniformToFill">
</Image>
i think the issue is in the LonglistSelctor itemsSource binding. anyone can help?
all code below(without the httpclient wrapper):
DetailsPage.cs is below:
public partial class DetailsPage : PhoneApplicationPage
{
DetailsPageViewModel viewModel = new DetailsPageViewModel();
public DetailsPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(DetailsPage_Loaded);
}
private void DetailsPage_Loaded(object sender, RoutedEventArgs e)
{
DataContext = viewModel;
//imageList.ItemsSource = viewModel.ImageUriCollection;
//imageList.ScrollTo(imageList.ItemsSource[imageList.ItemsSource.Count - 1]);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string ArticleId = "";
try
{
if (NavigationContext.QueryString.TryGetValue("ArticleId", out ArticleId))
{
Debug.WriteLine(ArticleId);
viewModel.LoadPage(Int32.Parse(ArticleId));
//Debug.WriteLine(viewModel.SpecifiedArticle.Words.ToString());
//LayoutRoot.DataContext = new CollectionViewSource { Source = viewModel.SpecifiedArticle };
}
else
{
MessageBox.Show("No ArticleId passed in.");
}
}
catch(Exception ex)
{
MessageBox.Show("Error in DetailsPage.OnNavigatedTo");
}
}
}
ViewModel is below:
public class DetailsPageViewModel : INotifyPropertyChanged
{
private bool _isLoading = false;
public bool IsLoading
{
get
{
return _isLoading;
}
set
{
_isLoading = value;
NotifyPropertyChanged("IsLoading");
}
}
public DetailsPageViewModel()
{
this.SpecifiedArticle = new Article();
this.ImageUriCollection = new ObservableCollection<Photo>();
this.IsLoading = false;
}
private Article specifiedArticle;
public Article SpecifiedArticle
{
get
{
return specifiedArticle;
}
set
{
specifiedArticle = value;
NotifyPropertyChanged("SpecifiedArticle");
}
}
public ObservableCollection<Photo> ImageUriCollection
{
get;
private set;
}
public void LoadPage(int articleId)
{
IsLoading = true;
ReadArticle(articleId);
}
private async void ReadArticle(int articleId)
{
try
{
Article articleDetails = new Article();
articleDetails = await CollectionHttpClient.GetAsyncByArticleId(articleId);
SpecifiedArticle = articleDetails;
//articleDetails.FirstImage = new Uri(articleDetails.ImagePathList[0]);
if (articleDetails.ImagePathList != null)
{
foreach (var item in articleDetails.ImagePathList)
{
Photo p = new Photo();
p.ImageSource = new Uri(item);
this.ImageUriCollection.Add(p);
}
//var image = await CollectionHttpClient.GetImageByImageName(articleDetails.ImagePath);
//Bytelist.Add(image);
}
else
{
this.ImageUriCollection = null;
}
IsLoading = false;
}
catch(Exception ex)
{
MessageBox.Show("sorry, no data.");
IsLoading = false;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
xaml is:
<phone:PhoneApplicationPage.Resources>
<vm:DetailsPageViewModel x:Key="viewModel"/>
<phone:JumpListItemBackgroundConverter x:Key="BackgroundConverter"/>
<phone:JumpListItemForegroundConverter x:Key="ForegroundConverter"/>
<Style x:Key="JumpListStyle" TargetType="phone:LongListSelector">
<Setter Property="LayoutMode" Value="List" />
<Setter Property="Margin" Value="12,12,0,0"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border Background="{Binding Converter={StaticResource BackgroundConverter}}"
Width="470"
Height="70"
Margin="6">
<TextBlock Text="{Binding Key}"
Foreground="{Binding Converter={StaticResource ForegroundConverter}}"
FontFamily="{StaticResource PhoneFontFamilySemiBold}"
FontSize="28"
Padding="2"
VerticalAlignment="Bottom"/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="GroupHeader">
<Border Background="Transparent">
<Border Background="Transparent" BorderBrush="Transparent" BorderThickness="1"
Width="400" Height="90"
HorizontalAlignment="Left">
<TextBlock Text="Pictures:"
Foreground="{StaticResource PhoneAccentBrush}"
FontSize="28"
Padding="2"
FontFamily="{StaticResource PhoneFontFamilySemiLight}"
HorizontalAlignment="Left"
VerticalAlignment="Center"/>
</Border>
</Border>
</DataTemplate>
<DataTemplate x:Key="ItemTemplate">
<StackPanel Height="108" Width="108" Margin="6,6">
<Image Width="108" Height="108" Stretch="UniformToFill">
<Image.Source>
<BitmapImage UriSource="{Binding ImageSource}" CreateOptions="BackgroundCreation"/>
</Image.Source>
</Image>
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="{Binding Path=SpecifiedArticle.Subject }" TextWrapping="Wrap" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place images here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Background="Transparent">
<!--
<Image Source="{Binding ImageUriCollection[0].ImageSource}" Width="108" Height="108" Stretch="UniformToFill">
<Image.Source>
<BitmapImage UriSource="{Binding ImageSource}" CreateOptions="BackgroundCreation"/>
</Image.Source>
</Image>
-->
<phone:LongListSelector Name="imageList" Margin="13,-30,0,0"
ItemsSource="{Binding ImageUriCollection}"
ItemTemplate="{StaticResource ItemTemplate}"
JumpListStyle="{StaticResource JumpListStyle}"
IsGroupingEnabled="True"
LayoutMode="Grid"
GridCellSize="108,108"/>
</Grid>
<!--ContentPanel - place article words here-->
<StackPanel Grid.Row="2" Margin="12,17,0,28">
<TextBlock Text="{Binding Path=SpecifiedArticle.Words}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
</Grid>
DetailsPage.cs is below:
public partial class DetailsPage : PhoneApplicationPage
{
DetailsPageViewModel viewModel = new DetailsPageViewModel();
public DetailsPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(DetailsPage_Loaded);
}
private void DetailsPage_Loaded(object sender, RoutedEventArgs e)
{
DataContext = viewModel;
//imageList.ItemsSource = viewModel.ImageUriCollection;
//imageList.ScrollTo(imageList.ItemsSource[imageList.ItemsSource.Count - 1]);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string ArticleId = "";
try
{
if (NavigationContext.QueryString.TryGetValue("ArticleId", out ArticleId))
{
Debug.WriteLine(ArticleId);
viewModel.LoadPage(Int32.Parse(ArticleId));
//Debug.WriteLine(viewModel.SpecifiedArticle.Words.ToString());
//LayoutRoot.DataContext = new CollectionViewSource { Source = viewModel.SpecifiedArticle };
}
else
{
MessageBox.Show("No ArticleId passed in.");
}
}
catch(Exception ex)
{
MessageBox.Show("Error in DetailsPage.OnNavigatedTo");
}
}
}
ViewModel is below:
public class DetailsPageViewModel : INotifyPropertyChanged
{
private bool _isLoading = false;
public bool IsLoading
{
get
{
return _isLoading;
}
set
{
_isLoading = value;
NotifyPropertyChanged("IsLoading");
}
}
public DetailsPageViewModel()
{
this.SpecifiedArticle = new Article();
this.ImageUriCollection = new ObservableCollection<Photo>();
this.IsLoading = false;
}
private Article specifiedArticle;
public Article SpecifiedArticle
{
get
{
return specifiedArticle;
}
set
{
specifiedArticle = value;
NotifyPropertyChanged("SpecifiedArticle");
}
}
public ObservableCollection<Photo> ImageUriCollection
{
get;
private set;
}
public void LoadPage(int articleId)
{
IsLoading = true;
ReadArticle(articleId);
}
private async void ReadArticle(int articleId)
{
try
{
Article articleDetails = new Article();
articleDetails = await CollectionHttpClient.GetAsyncByArticleId(articleId);
SpecifiedArticle = articleDetails;
//articleDetails.FirstImage = new Uri(articleDetails.ImagePathList[0]);
if (articleDetails.ImagePathList != null)
{
foreach (var item in articleDetails.ImagePathList)
{
Photo p = new Photo();
p.ImageSource = new Uri(item);
this.ImageUriCollection.Add(p);
}
//var image = await CollectionHttpClient.GetImageByImageName(articleDetails.ImagePath);
//Bytelist.Add(image);
}
else
{
this.ImageUriCollection = null;
}
IsLoading = false;
}
catch(Exception ex)
{
MessageBox.Show("sorry, no data.");
IsLoading = false;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Try this-
just change your ItemTemplate DataTemplate to a simpler one
If it works, add one change at a time.
<DataTemplate x:Key="ItemTemplate">
<Image Source="{Binding ImageSource}" Stretch="UniformToFill"/>
</DataTemplate>
it is the style problem.
delete the style, try again, images show well
<phone:LongListSelector Name="imageList" Margin="13,-30,0,0"
ItemsSource="{Binding ImageUriCollection}"
ItemTemplate="{StaticResource ItemTemplate}"
LayoutMode="Grid"
GridCellSize="108,108">
</phone:LongListSelector>
in the JumpListStyle, it contains the textblock which is not belong to the xaml, that is why the LonglistSelector does not display anything after binding collectly.

Why are the datagrid delete command buttons disabled?

I have a DataGrid where each row has a delete button. If I set the Command="Delete" attribute on the Delete button element in XAML, the button is disabled at runtime. Why??
XAML:
<Page x:Class="AxureExport.TargetPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:AxureExport"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
Title="Define Export Targets"
Loaded="Page_Loaded">
<Page.Resources>
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="DataGridCell_PreviewMouseLeftButtonDown" />
</Style>
</Page.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="7" />
<ColumnDefinition Width="65" />
<ColumnDefinition Width="458*" />
<ColumnDefinition Width="47" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="7" />
<RowDefinition Height="63*" />
</Grid.RowDefinitions>
<!-- target file -->
<TextBlock Name="textBlock3"
Text="2. Select the location where you want to save the string files:"
Height="23"
HorizontalAlignment="Left"
Margin="5,0,0,0"
VerticalAlignment="Top"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="4"
Width="441" />
<TextBlock Name="textBlock4"
Text="(Warning: This tool overwrites files in the target folder.)"
Height="16"
HorizontalAlignment="Left"
Margin="54,15,0,0"
VerticalAlignment="Top"
Width="256"
FontStyle="Italic"
FontWeight="Light"
FontSize="10"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2" />
<TextBlock Name="textBlock5"
Text="Target:"
Height="23"
HorizontalAlignment="Left"
Margin="22,30,0,238"
VerticalAlignment="Center"
Grid.Row="1"
Grid.Column="1" />
<!-- ExportTargets Datagrid -->
<DataGrid Name="ExportTargets"
AutoGenerateColumns="False"
Grid.Column="2"
Grid.Row="1"
Margin="0,71,0,63"
ItemsSource="{Binding Path=., Mode=TwoWay}"
SelectionUnit="Cell"
GridLinesVisibility="None"
RowDetailsVisibilityMode="Collapsed"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
RowHeaderWidth="40"
IsSynchronizedWithCurrentItem="True"
HorizontalScrollBarVisibility="Disabled"
CanUserAddRows="True"
CanUserDeleteRows="True">
<DataGrid.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}"
Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}"
Color="Black" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}"
Color="Black" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsXML, Mode=TwoWay}"
Header="XML"
CanUserSort="False"
CanUserReorder="False" />
<DataGridCheckBoxColumn Binding="{Binding IsProperty, Mode=TwoWay}"
Header="Property"
CanUserSort="False"
CanUserReorder="False" />
<DataGridTextColumn Binding="{Binding FolderName, Mode=TwoWay}"
Header="Folder"
Width="*"
CanUserReorder="False"
IsReadOnly="False" />
<DataGridTemplateColumn Header="Browse">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="Browse"
Command="{Binding BrowseCommand}"
HorizontalAlignment="Center"
Width="20">...</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Delete" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="Delete"
Command="{x:Static DataGrid.DeleteCommand}"
HorizontalAlignment="Center"
Width="20"
IsEnabled="True">X</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Name="NextButton"
Content="Next"
Grid.Column="2"
Grid.ColumnSpan="2"
Grid.Row="1"
Margin="0,0,2,12"
Width="100"
Height="40"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Click="NextButton_Click" />
</Grid>
</Page>
Code-behind:
// DataTable
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace AxureExport {
/// <summary>
/// Interaction logic for TargetPage.xaml
/// </summary>
public partial class TargetPage : Page {
FrameWindow _frame;
//ExportTargetInfos _eti;
public TargetPage(FrameWindow frame) {
InitializeComponent();
_frame = frame;
_frame.ExportTargets = new ExportTargetInfos(Properties.Settings.Default.ExportTargetHistory);
this.DataContext = _frame.ExportTargets;
}
//
// enable editing on single-click
private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
DataGridCell cell = sender as DataGridCell;
if (!cell.IsEditing) {
if (!cell.IsFocused)
cell.Focus();
if (!cell.IsSelected)
cell.IsSelected = true;
}
}
private void Page_Loaded(object sender, RoutedEventArgs e) {
}
private void NextButton_Click(object sender, RoutedEventArgs e) {
Properties.Settings.Default.ExportTargetHistory = _frame.ExportTargets.GetExportTargets();
_frame.NavigateTo(typeof(ExportPage));
}
}
}
ViewModel:
using System;
using System.Collections.ObjectModel; // ObservibleCollection<>
using System.Collections.Specialized; // StringCollection
using System.ComponentModel; // INotifyPropertyChanged
namespace AxureExport {
public class ExportTargetInfos : ObservableCollection<ExportTargetInfo> {
const char _SEP = '|'; // field seperator character
// default constructor
public ExportTargetInfos() : base() {
}
// copy constructors
public ExportTargetInfos(ExportTargetInfos etis) : base() {
if (etis == null) return;
foreach (ExportTargetInfo eti in etis) {
this.Add(new ExportTargetInfo(eti.FolderName, eti.IsXML, eti.IsProperty));
}
}
public ExportTargetInfos(StringCollection sc) : base() {
if (sc == null) return;
foreach (string s in sc)
Add(ParseExportTarget(s));
}
public StringCollection GetExportTargets() {
StringCollection sc = new StringCollection();
foreach (ExportTargetInfo et in this)
sc.Add(BuildExportTarget(et));
return sc;
}
// create a string from an ExportTarget (for persistance)
private static string BuildExportTarget(ExportTargetInfo et) {
return et.FolderName + _SEP + et.IsXML.ToString() + _SEP + et.IsProperty.ToString();
}
// create an ExportTarget from an unparsed string (for persistance)
private static ExportTargetInfo ParseExportTarget(string s) {
int count = s.Split(_SEP).Length - 1;
string[] items = s.Split(new[] { _SEP }, StringSplitOptions.None);
string name = (count < 1 ? String.Empty : items[0]);
bool isXML = (count > 0 ? Convert.ToBoolean(items[1]) : false);
bool isProp = (count > 1 ? Convert.ToBoolean(items[2]) : false);
return new ExportTargetInfo(name, isXML, isProp);
}
}
//
// represents an export target (folder plus options) in it's parsed form
public class ExportTargetInfo : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
public IDelegateCommand BrowseCommand { protected set; get; }
// default constructor is needed to enable datagrid to add rows
public ExportTargetInfo() {
_folderName = String.Empty;
_isXML = false;
_isProperty = false;
this.BrowseCommand = new DelegateCommand(ExecuteBrowse, CanExecuteBrowse);
}
public ExportTargetInfo(string targetFolderName, bool isXML, bool isProperties) {
_folderName = targetFolderName;
_isXML = isXML;
_isProperty = isProperties;
this.BrowseCommand = new DelegateCommand(ExecuteBrowse, CanExecuteBrowse);
}
private string _folderName;
public string FolderName {
get { return _folderName; }
set { SetProperty<string>(ref _folderName, value, #"FolderName"); }
}
private bool _isXML;
public bool IsXML {
get { return _isXML; }
set { SetProperty<bool>(ref _isXML, value, #"IsXML"); }
}
private bool _isProperty;
public bool IsProperty {
get { return _isProperty; }
set { SetProperty<bool>(ref _isProperty, value, #"IsProperty"); }
}
// browse button for selected row clicked
void ExecuteBrowse(object param) {
// use WPF-WinForms interop (:-p)
var folderDialog = new System.Windows.Forms.FolderBrowserDialog();
folderDialog.Description = "Please designate the target folder";
folderDialog.SelectedPath = FolderName;
folderDialog.ShowNewFolderButton = true;
if (folderDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
FolderName = folderDialog.SelectedPath;
}
bool CanExecuteBrowse(object param) {
return true;
}
protected bool SetProperty<T>(ref T storage, T value, string propertyName = null) {
if (object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName) {
if (PropertyChanged != null)
PropertyChanged( this, new PropertyChangedEventArgs(propertyName));
}
}
}
Does anyone have insight as to what's the cause?
This isn't a solution, but through trial & error I discovered the disabled delete button behavior is caused by the SelectionUnit="Cell" setting in the DataGrid. Setting it to FullRow enables the delete row button.
<Button Name="NextButton" isEnable = "true" />