I've an image structured in this way:
<Image Height="32" Width="32" Source="{Binding MatchController.Match.TeamHomeShield}" IsEnabled="False" />
and a label:
<Label Content="{Binding MatchController.Match.TeamHomeShield}" />
my problem's that I can't get the image displayed on the Image, but on the label I can see the value of TeamHomeShield, the property is created in this way:
private string _teamHomeShield;
public string TeamHomeShield
{
get { return _teamHomeShield; }
set
{
_teamHomeShield = value;
OnPropertyChanged();
}
}
private string _teamAwayShield;
public string TeamAwayShield
{
get { return _teamAwayShield; }
set
{
_teamAwayShield = value;
OnPropertyChanged();
}
}
why happen this?
Please check your Image source format
"/YourAssemblyName;component/YourPath/YourImage with extension"
xaml:
<Grid>
<Image Source="{Binding ImagePath}" Width="200" Height="100"/>
</Grid>
xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = objviewmodel;
objviewmodel.ImagePath = #"/ImageLoading;component/Assets/Desert.jpg"; // your image path
}
viewmodel objviewmodel = new viewmodel();
}
public class viewmodel : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnpropertyChanged([CallerMemberName] string PropertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
#endregion
private string _ImagePath=string.Empty;
public string ImagePath
{
get { return _ImagePath; }
set { _ImagePath = value; OnpropertyChanged(); }
}
}
You can use ImageSource property (not string) for binding images correctly.
For example:
public ImageSource ImagePath { get; set; }
Related
Is there a way to put a Page inside a <Grid/>, <StackPanel/>, <ContentControl/> or <ScrollViewer/> as content from code using a constructor call?
I expect such things:
XAML:
<Grid>
<ScrollViewer Content="{Binding Panel0}"/>
</Grid>
C#:
public class TestWindowViewModel : Page
{
public string Name { get; private set; }
public string Description { get; private set; }
public TestWindowViewModel(string name, string description)
{
Name = name;
Description = description;
}
}
_
public partial class SomeViewModel : Page
{
public TestWindowViewModel Panel0;
public SomeViewModel()
{
Panel0 = new TestWindowViewModel("panelName", "panelDescription");
InitializeComponent();
}
}
You can use a Frame tag
<ScrollViewer>
<Frame content = "{Binding MyPage}"/>
</ScrollViewer>
If you don't want to have a prop in your ViewModel then you should be able to do
<ScrollViewer>
<Frame>
<Frame.Content>
<locals:MyPage>
</Frame.Content>
</ScrollViewer>
Keep in mind you have something called TestWindowViewModel and it inherits Page. This is not a ViewModel. Instead it is a normal page.
You want something that looks like this:
public class NotifyPropertyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Page myPage;
public Page MyPage
{
get { return myPage; }
set
{
myPage = value;
NotifyPropertyChanged();
}
}
}
and you can go a level farther and make an abstract class:
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and then you can just inherit ViewModel like so:
public class TestWindow: Page
{
public TestWindow()
{
InitializeComponent();
}
}
public class TestWindowViewModel : ViewModel
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged();
}
}
private string description;
public string Description
{
get { return description; }
set
{
Description = value;
NotifyPropertyChanged();
}
}
}
Once you get this all seperated out correctly you can use the frame and do the same for the SomePage and SomePageViewModel and then you can use actual binding on the Frame Content from the ViewModel. I know this is long winded, but if you start out right on setting up a good MVVM setup you will save yourself headache if you ever get into Async and what not.
in xaml:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Frame x:Name="CurrentPage" NavigationUIVisibility="Hidden"></Frame>
</ScrollViewer>
in cs:
CurrentPage.Content = content;
where content is Page
I created a POC in WPF MVVM architecture.In which I used combo box control you can see in the code below.
<ComboBox Name="DeptCombo" Grid.Row="4" Grid.Column="1" ItemsSource="{Binding DepartmentList,Mode=TwoWay}" SelectedItem="{Binding Path=CurrentDepartment,Mode=TwoWay}" DisplayMemberPath="DepartmentName">
</ComboBox>
here CurrentDepartment is a property of Department class.
Everything is fine, I filled that combo , saved that combo value in the database, But the only problem I was facing is, I am not able to set the saved database value in that combo.I don't get any solution regarding that.Please help me.
Add UpdateSourceTrigger
SelectedItem="{Binding Path=CurrentDepartment,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Еhe code should look something like this:
class ViewModel: INotifyPropertyChanged
{
public ObservableCollection<string> Datas { get; set; } = new ObservableCollection<string>()
{
"FF", "AA", "BB"
};
private string currentItem;
public string CurrentItem
{
get => currentItem;
set
{
currentItem = value;
OnPropertyChanged("CurrentItem");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop="")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}
Example with Model:
PersonModel.cs:
class PersonModel
{
public string Name { get; set; }
public int Age { get; set; }
}
ViewModel.cs:
class ViewModel: INotifyPropertyChanged
{
public ObservableCollection<PersonModel> Datas { get; set; } = new ObservableCollection<PersonModel>()
{
new PersonModel(){Age = 10, Name="Tom"},
new PersonModel(){Age = 10, Name="Mark"},
};
private PersonModel currentItem;
public PersonModel CurrentItem
{
get => currentItem;
set
{
currentItem = value;
OnPropertyChanged("CurrentItem");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop="")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}
MainWindow.xaml:
<ComboBox ItemsSource="{Binding Datas}" SelectedItem="{Binding CurrentItem, UpdateSourceTrigger=PropertyChanged}"
Height="100" Width="100">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Department Class :
public class Department : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertychanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private int id;
public int Id
{
get { return id; }
set { id = value; OnPropertychanged("Id"); }
}
private string departmentName;
public string DepartmentName
{
get { return departmentName; }
set { departmentName = value; OnPropertychanged("DepartmentName"); }
}
private bool isActive;
public bool IsActive
{
get { return isActive; }
set { isActive = value; OnPropertychanged("IsActive"); }
}
}
View Model :
private ObservableCollection<Department> departmentList;
public ObservableCollection<Department> DepartmentList
{
get { return departmentList; }
set { departmentList = value; OnPropertyChanged("DepartmentList"); }
}
private Department currentDepartment;
public Department CurrentDepartment
{
get { return currentDepartment; }
set { currentDepartment = value; OnPropertyChanged("CurrentDepartment"); }
}
private void DepartmentPop()
{
DepartmentList = new ObservableCollection<Department>
(objDepartmentService.GetAll());
}
I'm trying to make the TextBox show the stringBody property of the CurrentDialog property of the window. Here's the XAML part:
<TextBox x:Name="ComposerBox" Height="90.302" Margin="311,0,141.355,10"
...
Text="{Binding Body}"
ScrollViewer.CanContentScroll="True" SpellCheck.IsEnabled="True"
VerticalAlignment="Bottom">
Here is a string from the windows constructor:
MessagingBox.DataContext = CurrentDialog;
I've also tried to set DataContext to this with no result.
Here's how CurrentDialog is defined:
private MessageDialog CurrentDialog { get; set; }
Here's the MessageDialog class definition:
[Serializable][DataContract]
public class MessageDialog
{
public string Name { get; private set; }
public UserData User { get; private set; }
private List<Message> Dialog = new List<Message>();
public string Body { get; private set; }
public MessageDialog(UserData data)
{
Name = data.Username;
User = data;
Body = "";
}
public void Add(Message msg)
{
Dialog.Add(msg);
Body += $"{msg.From}: {msg.Body} \n\n";
}
}
}
The binding doesn't work at all. I also want it to be one-way.
Text="{Binding CurrentPerson.Body}"
Not sure why the binding path contains CurrentPerson, when it should be CurrentDialog, but even that isn't supposed to be there. Since the DataContext is already set to CurrentDialog, you can simply bind the text to :
Text="{Binding Body}"
You need to implement INotifyPropertyChanged, so the WPF know when the property changed:
[Serializable][DataContract]
public class MessageDialog : INotifyPropertyChanged
{
#region public string Body
private string m_Body;
public string Body
{
get { return m_Body; }
private set
{
if (m_Body == value)
return;
m_Body = value;
this.NotifyPropertyChanged();
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
I have a listbox wit listboxitems with an image and a textblock.
The listbox has a custom class as datasource.
What I want is when the listboxItem is tapped. The image inside the listboxitem changes.
Here is what I have so far:
My custom class:
public class MemberUser
{
[JsonProperty("member_id", NullValueHandling = NullValueHandling.Ignore)]
public int member_id { get; private set; }
[JsonProperty("first_name", NullValueHandling = NullValueHandling.Ignore)]
public String first_name { get; private set; }
[JsonProperty("last_name", NullValueHandling = NullValueHandling.Ignore)]
public String last_name { get; private set; }
public string fullName
{
get
{
return String.Format("{0} {1}", first_name, last_name);
}
}
public bool selected{get;set;}
public string selectedImage
{
get{
if (selected)
{
return "/Assets/ic_selected.png";
}
else
{
return "/Assets/ic_not_selected.png";
}
}
}
}
My codebehind file (Only the code that you need to understand)
private OrganizationObject community;
private IEnumerable<MemberUser> memItems;
private List<MemberUser> notFoundEvents = new List<MemberUser>();
public EventAdd()
{
InitializeComponent();
BindData();
}
private async void BindData()
{
try
{
memItems = MemberDataSource.memberList;
if (memItems.Count() == 0)
{
await MemberDataSource.GetLocalMember();
memItems = MemberDataSource.memberList;
}
inviteList.DataContext = memItems;
/* foreach (MemberUser obj in memItems)
{
if (obj.accepted == 1)
{
inviteList.Items.Add(obj);
}
}*/
}
catch (KeyNotFoundException)
{
NavigationService.GoBack();
}
}
private void Selectionchanged_Eventhandler_of_Listbox(object sender, SelectionChangedEventArgs e)
{
MemberUser myobject = (sender as ListBox).SelectedItem as MemberUser;
if (myobject.selected)
{
myobject.selected = false;
}
else
{
myobject.selected = true;
}
}
My XAML
<ListBox x:Name="inviteList" ItemsSource="{Binding}" Margin="20,0,0,0" SelectionChanged="Selectionchanged_Eventhandler_of_Listbox">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="80" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="0.70*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Grid.Row="0" x:Name="img_selected" Source="{Binding selectedImage}" Width="26" Height="29"></Image>
<TextBlock Grid.Column="1" Grid.Row="0" x:Name="fullName" Text="{Binding fullName}" Foreground="#FF4C6383" FontFamily="/Membr;component/Assets/Fonts/Fonts.zip#Source Sans Pro" Height="50" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So what am i missing? Also it seems like I can only tap once on each list item?
Please help !
You can achieve it with few ways:
1) Inherit you custom class from INotifyPropertyChanged
public class MemberUser : INotifyPropertyChanged
{
public int member_id { get; private set; }
public String first_name { get; private set; }
public String last_name { get; private set; }
public string fullName
{
get
{
return String.Format("{0} {1}", first_name, last_name);
}
}
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
OnPropertyChanged("selectedImage");
}
}
private bool _isSelected;
public string selectedImage
{
get
{
if (IsSelected)
{
return "/Assets/ic_selected.png";
}
else
{
return "/Assets/ic_not_selected.png";
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
2) Bind ImageSource to IsSelected property through Converter
<Image Grid.Column="0" Grid.Row="0" x:Name="img_selected" Source="{Binding IsSelected, Converter={StaticResource SelectedImageConverter}}" Width="26" Height="29"></Image>
public class SelectedImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
var isSelected = (bool)value;
return isSelected
? "/Assets/ic_selected.png"
: "/Assets/ic_not_selected.png";
}
catch (Exception)
{
return "/Assets/ic_not_selected.png";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
If the problem isn't the images path, I think you need to implement INotifyPropertyChanged interface, in the MemberUser class, and when you set the Selected property, notify also the change of SelectedImage property. Something like:
private bool _selected;
public bool Selected
{
get{ return _selected;}
set
{
if(value!=_selected)
{
_selected=value;
OnPropertyChanged("Selected");
OnPropertyChanged("SelectedImage");
}
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName="")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Try
public ImageSource selectedImage
{
ImageSource temp;
get{
if (selected)
{
temp=new BitmapImage(new Uri("ms-appx:////Assets/ic_selected.png", UriKind.RelativeOrAbsolute));
return temp ;
}
else
{
temp=new BitmapImage(new Uri("/Assets/ic_not_selected.png", UriKind.RelativeOrAbsolute));
return temp ;
}
}
}
Since Source of image Binds to an ImageSource not to string.
Check if your url requires ms-appx://// or not. (it does in windows 8.1 apps) Also You need to add an INotifyProperty Chaged extension to be able to see the property being changed instantly.
The following in my code in MainWindow.xaml.cs:
namespace Test
{
public partial class MainWindow : Window
{
public class ChannelInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _channelDescription;
public string ChannelDescription
{
get
{
return _channelDescription;
}
set
{
if (value != _channelDescription)
{
_channelDescription = value;
Notify("ChannelDescription");
}
}
}
}
public ObservableCollection<ChannelInfo> Channels { get; set; }
public MainWindow()
{
InitializeComponent();
Channels = new ObservableCollection<ChannelInfo>()
{
new ChannelInfo() { ChannelDescription = "Ib" }
};
DataContext = Channels;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//((ObservableCollection<ChannelInfo>)DataContext).Add(new ChannelInfo() { ChannelDescription = "Ib" });
}
}
}
In my XAML I have a TextBox defined as follows:
<TextBox Height="23" Text="{Binding ChannelDescription}" HorizontalAlignment="Left" Margin="180,106,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
Now my problem is if I add an item to Channels in the constructor itself, then the TextBox is displaying the bound text. But when I add it in the Window_Loaded like above (uncomment the line), the Text is not displayed.