I am using PRISM MVVM to show a Listview of files containing an image, a filename and the size of the image.
The user should be able to change the name of the file by typing in a new name.
When leaving the text box the filename in my ViewModel should be renamed. For this i need of course to know the Text as it was before and after.
I don't want to use Code behind, but I guess I need to work with GotFocus to store the value before and on LostFocus to get the new value. Right?
This is my XAML
<Grid>
<ListView x:Name="MiscFilesListView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding MiscFiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" VerticalAlignment="Top" HorizontalAlignment="Stretch">
<Image Source="{Binding ImageData}" HorizontalAlignment="Center" VerticalAlignment="Top" Height="100" Width="100" />
<TextBox Text="{Binding FileName}" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding Size}" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
The Listview is bound to:
public ObservableCollection<MiscFile> MiscFiles
{
get => _miscFiles;
set => SetProperty(ref _miscFiles, value);
}
The viewmodel
public class MiscFile : INotifyPropertyChanged
{
public BitmapImage ImageData { get; set; }
public string FileName { get; set; }
public string FullFileName { get; set; }
public string Size { get; set; }
public void OnPropertyChanged(string propertyname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Any idea how I can achieve this inside the Viewmodel?
Do I need some sort of EventTrigger?
You could make a private field for the filename in the viewmodel. The public FileName property should check if the value is different from the value set in the private field. Also notify the INotifyPropertyChanged by calling OnPropertyChanged.
Doing this should update the filename property.
If you want to keep the old filename it is possible to call the Path.GetFileName(FullFileName) method of the static Path class.
private string _filename;
public BitmapImage ImageData
{
get;set;
}
public string FileName
{
get
{
return _filename;
}
set
{
if (_filename != value)
{
_filename = value;
OnPropertyChanged(nameof(FileName));
}
}
}
The previous value is already stored in your MiscFile object. Just define a backing field for your property:
private string _filename;
public string FileName
{
get { return _filename; }
set
{
string oldValue = _filename;
string newValue = value;
//update...
_filename = value;
}
}
This should work as the source property shouldn't be set until the TextBox loses focus since you haven't changed the UpdateSourceTrigger property of the binding from its default value of LostFocus:
<TextBox Text="{Binding FileName, UpdateSourceTrigger=LostFocus}" ... />
Related
Problem: When location is changed via ComboBox cb1 the related location TextBlock does not change to updated value.
I am self learning and below is experiment code on binding that has
public EmpDeptViewModel vm; its initialize on button click event as below
private void btn2_Click(object sender, RoutedEventArgs e) {
vm = new EmpDeptViewModel();
this.Bindings.Update(); }
The XAML looks like this.
<ListView x:Name="listview3" ItemsSource="{x:Bind vm.InstanceOfDepartmentData}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="classes:Department">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5">
<Run Text="DeptNo: " /><Run Text="{x:Bind DeptNo}" />
</TextBlock>
<TextBlock Margin="5">
<Run Text="DeptName: " /><Run Text="{x:Bind DeptName}" />
</TextBlock>
<TextBlock Margin="5">
<Run Text="Location: " /><Run Text="{x:Bind Location, Mode=OneWay}" />
</TextBlock>
<ComboBox x:Name="cb1" ItemsSource="{Binding Source={StaticResource MyLocatonList}, Path=ListofLocationsInsideViewModel, Mode=TwoWay}" DisplayMemberPath="LocationName" SelectedValuePath="LocationName" SelectedValue="{x:Bind Location}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Problem: When location is changed via ComboBox cb1 the related location TextBlock does not change to updated value.
The x:DataType="classes:Department" looks like this.
public class Department : BindableBase
{ private string _location;
public Department(int pdeptNo, string pdeptName, string plocation)
{
DeptNo = pdeptNo;
DeptName = pdeptName;
Location = plocation;
ListOfDeparmentEmployees = new List<Employee>(); }
public int DeptNo { get; set; }
public string DeptName { get; set; }
public string Location {
get { return this._location; }
set { this.SetProperty(ref this._location, value); }
}
public List<Employee> ListOfDeparmentEmployees { get; set; }
}
You may be bound in the wrong position
In your ComboBox, you set TwoWay to the ItemsSource. This does not make sense. You cannot change the Location if you modify the value of the ComboBox.
Try this:
Xaml
...
<ComboBox x:Name="cb1" ItemsSource="{Binding Source={StaticResource MyLocatonList}, Path=ListofLocationsInsideViewModel}"
DisplayMemberPath="LocationName" SelectedValuePath="LocationName" SelectedValue="{x:Bind Location,Mode=TwoWay}" />
...
However, if you write it directly, it will cause an endless loop and then report an error. You need to rewrite the Location property of the Department class.
Department.cs
...
public string Location
{
get { return this._location; }
set
{
if (_location != value)
{
this.SetProperty(ref this._location, value);
}
}
}
...
In addition, please note whether your BindableBase base class implements the INotifyPropertyChanged interface, which is the basis for modifying the UI while modifying the data.
Best regards.
I am having an issue with my app getting it updated for 10. It worked in windows 8/8.1 although I think I may have been getting it there as well but windows 8 it didn't crash. Basically I have data I'm pulling from online and when I look at it, it all seems correct. The only thing is that I have an observable collection where I have defined the parameters/variables (and since I use the same gridview for two different parts in my app sometimes I have certain fields as null since they aren't being used.)
Here is my base Class
public class sTumblrblog_gv : INotifyPropertyChanged
{
private string title;
public string Title
{
get
{
return title;
}
set
{
title = value;
NotifyPropertyChanged();
}
}
private string url;
public string Url
{
get
{
return url;
}
set
{
url = value;
NotifyPropertyChanged();
}
}
private string avatarimage;
public string AvatarImage
{
get
{
return avatarimage;
}
set
{
avatarimage = value;
NotifyPropertyChanged();
}
}
private string blogposts;
public string BlogPosts
{
get
{
return blogposts;
}
set
{
blogposts = value;
NotifyPropertyChanged();
}
}
private bool isnsfw;
public bool IsNsfw
{
get
{
return isnsfw;
}
set
{
isnsfw = value;
NotifyPropertyChanged();
}
}
private BitmapImage dlimage;
public BitmapImage DLImage
{
get
{
return dlimage;
}
set
{
dlimage = value;
NotifyPropertyChanged();
}
}
public string ImgName { get; set; }
public sTumblrblog_gv()
{
//string Title;
//string Url;
//string AvatarImage;
//string BlogPosts;
//bool IsNsfw;
}
public sTumblrblog_gv(IRandomAccessStream stream, string imgname)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(stream);
DLImage = bmp;
ImgName = imgname;
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I use this to create the collection:
ObservableCollection<sTumblrblog_gv> sTumblrblog_gv_list = newObservableCollection<sTumblrblog_gv>();
Here is my data template for the gridview:
<DataTemplate x:Key="imageTemplate">
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<!-- <WrapGrid Orientation="Horizontal"/>-->
<Border Height="100" Width="100" >
<Image Source="{Binding AvatarImage}" Stretch="UniformToFill" ImageFailed="Photo_ImageFailed" />
</Border>
<StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="10">
<Image Source="{Binding DLImage}" MaxHeight="100" ImageFailed="Photo_ImageFailed" />
<TextBlock Margin="10,0,0,0" Text="{Binding Title}" FontSize="12" />
<TextBlock Margin="10,2,0,0" Text="{Binding Url}" FontSize="12" />
<TextBlock Margin="10,2,0,0" Text="{Binding BlogPosts}" FontSize="12" />
<CheckBox IsChecked="{Binding IsNsfw}" x:Uid="Nsfw" />
<!-- <Image Source="{Binding Path=Thumbnail,Converter={StaticResource imageConverter}}" MaxHeight="100" />-->
</StackPanel>
</StackPanel>
</ItemsWrapGrid>
</ItemsPanelTemplate>
</DataTemplate>
<CollectionViewSource x:Name="sTumblrCVS"/>
When I step through the parts I get to the section where I actually add elements to the gridview and that's where it crashes. I've tried using dispatcher to avoid any UI thread vs background thread type issues.
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
sTumblrblog_gv_list.Add(new sTumblrblog_gv() { Title = tumblrusrfollow.Title, Url = tumblrusrfollow.Url.ToString(), AvatarImage = imageavatar.ToString(), BlogPosts = blogposts, IsNsfw = blogisnfw, DLImage = null, ImgName = "" });
});
And it crashes right after I do the add with the Exception of "The Parameter is incorrect" as a {Windows.UI.Xaml.UnhandledExceptionEventArgs} -
Native View 0x0ea7ea20 {...} IUnknown *
{Windows.UI.Xaml.dll!ctl::ComObject}
[0] 0x52b58210
{Windows.UI.Xaml.dll!ctl::ComObject::QueryInterface(const
_GUID &, void * *)} void *
Which after playing with this for a few days leads me to believe there's something with the data I'm sending in as the source.
But the data which I've even enumerated before sending it into the Observable collection matches the types and fields that should be. (I don't have any errors according visual studio, with matching types etc..)
did something change between 8 and 10 with gridview's?
am showing list of movie as a panorama for my windows phone 7 app. on click on each movie am showing the movie details,cast.
the movie details , cast am showing as a pivot control. movie details works fine
But when i got to show cast as , it doesnt work. i have list of cast objects. and am binding the source to a listbox in the cast pivot control , but it doesnt show any data. Please help me. below are the classes i have used. Thank you
MainViewModel.cs
public class MainViewModel
{
public ObservableCollection<ItemViewModel> MovieItems { get; set; }
}
ItemViewModel.cs
public class ItemViewModel : INotifyPropertyChanged
{
private string _title;
public string _Title
{
get { return _title; }
set
{
if (value != _title)
{
_title = value;
NotifyPropertyChanged("title");
}
}
}
private ObservableCollection<Cast> _cast;
public ObservableCollection<Cast> _Cast
{
get { return _cast; }
set
{
if (value != _cast)
{
_cast = value;
NotifyPropertyChanged("Cast");
}
}
}
..........
}
Cast.cs
public class Cast
{
public string name { get; set; }
public string imagesource { get; set; }
public Cast(string _name, string _imagesource)
{
this.imagesource = _imagesource;
this.name = _name;
}
}
for each movie i have a list of cast objects
MovieModel.cs
App.Model.MovieItems.Add(
new ItemViewModel()
{
_Title = data["title"].ToString(),
_Cast=casObs,
........
}
);
moviedetails.xaml
<ListBox Name="ListBox" Margin="0,0,-12,0" ItemsSource="{Binding _Cast}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="78">
<Canvas>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="120,5,60,3" Text="{Binding name}" TextWrapping="Wrap" FontSize="32" Style="{StaticResource PhoneTextNormalStyle}"/>
<Image Height="90" HorizontalAlignment="Left" Margin="12,10,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="90" Source="{Binding imagesource}" />
</Canvas>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Replace
NotifyPropertyChanged("title");
to
NotifyPropertyChanged("_Title");
And
NotifyPropertyChanged("Cast");
to
NotifyPropertyChanged("_Cast");
Hope it's help.
apply converter inbetween, where you need to return ImageSource/BitmapImage, instead of string
I am having trouble binding my View Model to my View. I am a beginner with MVVM, but I believe I am implementing my system (almost) correctly. I have a Model that contains data, which I am getting in my View Model, and then when my page is navigated to, I am attempting to grab that View Model data and binding it to the View.
My issue is that I have a ListBox in my View with 3 objects per item, and I cannot seem to bind to it correctly for each item in my list.
MainPage.xaml
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}"
SelectionChanged="FavoritesListBox_SelectionChanged">
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}"
Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}"
FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress"
Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</ListBox>
MainPage.xaml.cs
public FavoritesPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
FavoritesListBox.DataContext = App.ViewModel;
}
App.xaml.cs
private static MainViewModel viewModel = null;
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
MainViewModel.cs
public ObservableCollection<ItemViewModel> FavoriteItems { get; private set; }
public MainViewModel()
{
//FavoriteItems = new ObservableCollection<ItemViewModel>();
FavoriteItems = Settings.FavoritesList.Value;
}
Settings.cs (The Model)
public static Setting<ObservableCollection<ItemViewModel>> FavoritesList =
new Setting<ObservableCollection<ItemViewModel>>(
"Favorites",
new ObservableCollection<ItemViewModel>());
ItemViewModel.cs
private string _favicon;
public string Favicon
{
get
{
return _favicon;
}
set
{
if (value != _favicon)
{
_favicon = value;
NotifyPropertyChanged("Favicon");
}
}
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
private string _address;
public string Address
{
get
{
return _address;
}
set
{
if (value != _address)
{
_address = value;
NotifyPropertyChanged("Address");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
..and this is where and how I am saving each item (which should have three properties listed in the ItemViewModel
void addToFavorites_Click(object sender, EventArgs e)
{
var favoriteItem =
new ItemViewModel{
Favicon = "",
Name = "",
Address = TheBrowser.currentUrl() };
Settings.FavoritesList.Value.Add(favoriteItem);
}
Where FavoritesList is populated using an ItemViewModel containing 3 objects. The list is being populated correctly because during debugging I can see the entities in FavoritesList, but I am having an issue calling these entities in the view model to show up in my ListBox in the view?
I believe I am binding incorrectly but I'm not sure how to fix this?
In your XAML you bind to paths Name and Address do you have these 2 properties defined in your ItemViewModel?
Update after reading your code properly:
You are not updating the datatemplate of the Items of the Listbox. This is what you need to do:
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}" SelectionChanged="FavoritesListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}" Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress" Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In addition to setting your DataContext to your viewmodel, (as mentioned in the comment linking to Creating ContextBinding XAML) , you also need to have your view model implement INotifyPropertyChanged (including ItemViewModel, which you don't show in your question)
Hi all i have problem in this code, please help me..
I have view
<StackPanel Orientation="Horizontal" Margin="3">
<Label Content="Audit Type" MinWidth="100"/>
<Label Content=":"/>
<StackPanel Orientation="Vertical">
<ListBox ItemsSource="{Binding Items}" Margin="3" SelectionMode="Extended" MinWidth="180">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="check" Content="{Binding Value}" IsChecked="{Binding IsChecked, Mode=TwoWay}" Margin="3" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</StackPanel>
and for View model
private List<AuditTypeExport> _items;
private List<string> _value;
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public List<AuditTypeExport> Items
{
get { return _items; }
}
public List<string> Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged("Value");
}
}
And ViewModel Constractor
_items = _model.GetAuditType();
_value = _model.GetAuditType().Select(item => item.Name).ToList();
For your information
public class AuditTypeExport
{
private int _id;
private string _name;
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
The result : checkbox appeares, but the content doesn't and I don't have a clue why.
Question Number 2 : I want to get the value back, how can I do that?
Thank you
It is unclear how you are using your ViewModel. Is that bound to the form? Or each item in the ListBox?
It looks like your ListBox is bound to the Items collection of your VM, so the ItemTemplate will be used with a AuditTypeExport as the data context. You are binding to "Value" and "IsChecked" properties which do not exist on the AuditTypeExport class.
What you are trying to do here is bind a property of type List<String> Value to CheckBox's Content property which is of type Object.
To simplify, you are assigning a collection of strings to a string. Which is not a good thing. And that is why it does not work.
Try using ItemsControl to show Value property or use an IValueConverter to convert List<String> to comma separated string.