How to bind a control value to another source - silverlight

I have a ListView which is populated with a collection called Files(ObservableCollection FileViewModel ), also I have another SelectedFiles(List Guid ) which hold the selected files id in it, how can I bind this to the UI to show the selected files with checkbox control.
Xaml:
<ListView Grid.Column="0" Grid.Row="2" Name="lstSourceFiles" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<CheckBox></CheckBox>
<TextBlock Text="{Binding Name}"></TextBlock>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code:
public List<Guid> SelectedSourceFiles { get; set; }
public ObservableCollection<FileViewModel> Files { get; set; }
public class FileViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public partial class MainWindow : Window
{
public List<Guid> SelectedSourceFiles { get; set; }
public MainWindow()
{
AddHandler(TreeViewItem.SelectedEvent, new RoutedEventHandler(TreeItemSelected), true);
}
private void TreeItemSelected(object sender, RoutedEventArgs e)
{
var item = e.OriginalSource as TreeViewItem;
if (item == null)
{ return; }
var folder = item.DataContext as FolderViewModel;
if (folder == null)
{ return; }
if (!folder.IsFilesLoaded)
{
FileManager.LoadFiles(folder);
}
lstSourceFiles.ItemsSource = folder.Files;
}
}

The easiest way would be to add an "IsSelected" property to the view model:
public class FileViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
Loop through and set the property:
foreach (var file in folder.Files)
file.IsSelected = (SelectedSourceFiles.Contains(file.Guid);
And then of course bind to it:
<CheckBox IsChecked="{Binding IsSelected}" />
An alternate to the above would be to bind to the GUID and use IValueConverter that checks against the the selected list.

Related

EF6, Code First. How to set alternative data source for GridControl column using XAML Devexpress

I have two related entities:
public class Event
{
public string ID { get; set; }
public DateTime EventDate { get; set; }
public string EventData { get; set; }
public string DocID1 { get; set; }
public int DocID2 { get; set; }
public virtual Document Document1 { get; set; }
public virtual Document Document2 { get; set; }
}
public class Document
{
public string ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Number { get; set; }
}
Also created List to display data in WPF:
private ObservableCollection<Event> eventList;
public ObservableCollection<Event> EventList
{
get { return eventList; }
set
{
if (value != eventList)
{
eventList = value;
}
RaisePropertyChanged(nameof(EventList));
}
}
data for my EventList is taken from entity Event:
var query = DBContext.Events.Select(x => x).AsQueryable();
EventList = query.ToList();
What I need is how to set GridControl Column "colFirstName" value to be equal "Document1.FirstName" if DocID1 is not null and "Document2.FirstName" if DocID2 is not null. My XAML code for GridControl is below, could You help how to do it in Xaml, not in ViewModel, or if there is no way to do it in Xaml, what is the best way to do it in ViewModel.
<dxg:GridControl AutoPopulateColumns="False"
ItemsSource="{Binding EventList, UpdateSourceTrigger=PropertyChanged}">
<dxg:GridControl.Columns>
<dxg:GridColumn
x:Name="colEventData"
Width="120"
FieldName="EventData"
Header =" Event data"
Visible="True" >
</dxg:GridColumn>
<dxg:GridColumn
x:Name="colFirsnName"
Width="120"
FieldName="Document1.FirstName"
Header="First Name"
Visible="True"
VisibleIndex="1" />
</dxg:GridControl.Columns>
</dxg:GridControl>
You can use the UnboundExpression property to create a column with a custom expression. For example, to conditionally show values from different properties, use the Iif function:
<dxg:GridColumn FieldName="CustomColumn" x:Name="colFirsnName"
UnboundExpression="Iif(IsNullOrEmpty([DocID1]), [Document2.FirstName], [Document1.FirstName])"
UnboundType="String"/>
This kind of logic should be implemented in the model or view model class. Remember that XAML is only a markup language.
Just create another property that returns the first match and bind to this one. If your entity classes are auto-generated, you could create a new partial class:
public partial class Event
{
public string DocName
{
get
{
if (Document1 != null)
return Document1.FirstName;
if (Document2 != null)
return Document1.LastName;
return null;
}
}
}
<dxg:GridColumn
x:Name="colFirsnName"
Width="120"
FieldName="DocName"
Header="First Name"
Visible="True"
VisibleIndex="1" IsReadOnly="true" />

Select one combo box value on the basis of another combo box value

I am new to WPF application development. I want to get floors on the basis of blocks. I have one combo box of blocks and another combo box of floors. When I select any block in one combo box, the other combo box should display the floors of the select block.
This is the combo box layout:
<ComboBox Grid.Row="0" Grid.Column="0" Width="100"
Margin="0,0,0,10" Height="35"
Loaded="FrameworkElement_OnLoaded"
SelectedValuePath ="Id"
SelectedValue ="{Binding SelectedBlockId, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectionChanged="Selector_OnSelectionChanged" ItemsSource="{Binding Blocks}" />
<ComboBox Grid.Row="0" Grid.Column="3" Width="100"
Margin="0,0,0,10" Height="35"
Loaded="FrameworkElement_OnLoaded"
SelectedValuePath ="Id"
SelectedValue ="{Binding SelectedFloorId, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectionChanged="Selector_OnSelectionChanged" ItemsSource="{Binding Floors}" />
Here's a slightly different example.
Xaml...
<!-- language: xaml -->
<Label Name="FavoriteFoodLbl" Grid.Column="0" Grid.Row="13">Favorite Food</Label>
<ComboBox Name="FavoriteFoodCombo" Grid.Column="1" Grid.Row="13" ItemsSource="{Binding Foods}" SelectedItem="{Binding FavoriteFood, UpdateSourceTrigger=PropertyChanged}" />
<Label Name="FavoriteFlavourLbl" Grid.Column="2" Grid.Row="13">Favorite Flavour</Label>
<ComboBox Name="FavoriteFlavourCombo" Grid.Column="3" Grid.Row="13" ItemsSource="{Binding Flavours}" />
View model/code-behind code...
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> Foods { get; set; } = new ObservableCollection<string>() { "Pizza", "Ice Cream", "Soup" };
private string _favoriteFood;
public string FavoriteFood
{
get { return _favoriteFood; }
set
{
_favoriteFood = value;
switch (_favoriteFood)
{
case "Pizza":
_flavours = new ObservableCollection<string>(PizzaToppings);
break;
case "Ice Cream":
_flavours = new ObservableCollection<string>(IceCreamFlavours);
break;
case "Soup":
_flavours = new ObservableCollection<string>(SoupFlavours);
break;
default:
_flavours = new ObservableCollection<string>();
break;
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FavoriteFood"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Flavours"));
}
}
public List<string> PizzaToppings { get; set; } = new List<string>() { "Margarita", "Pepperoni", "Meat Feast" };
public List<string> IceCreamFlavours { get; set; } = new List<string>() { "Vanilla", "Strawberry", "Chocolate" };
public List<string> SoupFlavours { get; set; } = new List<string>() { "Tomato", "Leek and Potato", "Chicken" };
private ObservableCollection<string> _flavours = null;
public ObservableCollection<string> Flavours
{
get
{
return _flavours;
}
set
{
_flavours = value;
RaisePropertyChanged();
}
}
public string FavoriteFlavour { get; set; }
Change out the case statement for something appropriate.
Does this help?
What you need to do is to establish master-detail relationship between Block and Floor models and correctly bind them to you View (comboboxes).
So supposing your Floor and Block have two properties ID and Description, your model should look like this:
public class Floor
{
public int Id { get; set; }
public string Description { get; set; }
}
public class Block
{
public int Id { get; set; }
public int Description { get; set; }
// notice Floors collection inside each block
public IList<Floor> Floors { get; set; }
public Block()
{
Floors = new List<Floor>();
}
}
Your ViewModel will contain two ObservableCollections and one property to store currently selected block. Notice SelectedBlock property setter: when the property is updated, the Floors collection gets recreated with new values.
public const string BlocksPropertyName = "Blocks";
private ObservableCollection<Block> _blocks = null;
public ObservableCollection<Block> Blocks
{
get
{
return _blocks;
}
set
{
_blocks = value;
RaisePropertyChanged(BlocksPropertyName);
}
}
public const string SelectedBlockPropertyName = "SelectedBlock";
private Block _selectedBlock = null;
public Block SelectedBlock
{
get
{
return _selectedBlock;
}
set
{
_selectedBlock = value;
RaisePropertyChanged(SelectedBlockPropertyName);
if (_selectedBlock != null)
{
Floors = new ObservableCollection<Floor>(_selectedBlock.Floors);
}
}
}
public const string FloorsPropertyName = "Floors";
private ObservableCollection<Floor> _floors = null;
public ObservableCollection<Floor> Floors
{
get
{
return _floors;
}
set
{
_floors = value;
RaisePropertyChanged(FloorsPropertyName);
}
}
In your XAML you just bind both ComboBoxes to corrispective collections:
<ComboBox ItemsSource="{Binding Blocks}"
SelectedItem="{Binding SelectedBlock}" />
<ComboBox ItemsSource="{Binding Floors}" />
Please refer to the following blog post for an example of how to implement cascading ComboBoxes in WPF using the MVVM pattern: https://blog.magnusmontin.net/2013/06/17/cascading-comboboxes-in-wpf-using-mvvm/
You could basically just replace the Countries and the Cities types from the sample code with your Block and Floor types.

MahApps.Metro SplitButton SelectedItem Databinding

The View is:
<Controls:SplitButton Margin="217,409.75,56,185" Name="SplitButton1"
Width="384"
HorizontalAlignment="Center"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Orientation="Vertical"
DisplayMemberPath ="UserName"
SelectedItem="{Binding SelectedUser,UpdateSourceTrigger=PropertyChanged,Mode=OneWay}"
ItemsSource="{Binding Users, Mode=TwoWay}" />
The ViewModel is:
public string SelectedUser
{
get { return selectedUser; }
set
{
selectedUser = value;
RaisePropertyChanged("SelectedUser");
}
}
public ObservableCollection<UserModel> Users
{
get
{
return users;
}
set
{
users = value;
}
}
the Model is:
public class UserModel
{
private int id;
private string userName;
private int groupId;
private string deviceMacAddress;
public int Id { get; set; }
public string UserName { get; set; }
public int GroupId { get; set; }
public string DeviceMacAddress { get; set; }
}
i use the above code in xaml to bind the selectedItem in the splitbutton to ViewModel->property--SelectedUser.
but it does not work. anyone knows why?
SelectedUser is returned as Model name (PresentationLayer.Model.UserModel) instead of UserName prooperty.
Because your binding is OneWay by your definition.
Set your binding to TwoWay.
<Controls:SplitButton SelectedItem="{Binding SelectedUser,Mode=TwoWay}"/>
And, there is no need to set the UpdateSourceTrigger=PropertyChanged in this case, because the UpdateSourceTrigger is PropertyChanged by default for the SelectedItem property.

Updating ObservableCollection not Updating View on Command

I have a ListView which binds to an ObservableCollection. When my button's command updates the item in the collection, the View is not updated. I am also using Fody to implement INotifyPropertyChanged throughout.
View
<ListView ItemsSource="{Binding Path=DatabaseInfos}" VerticalAlignment="Stretch" Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Header="Current Version">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding CurrentVerion}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Test"
Command="{Binding DataContext.CmdButtonClicked, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
CommandParameter="{Binding}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
ViewModel
public interface IDatabaseViewModel
{
ObservableCollection<DatabaseInfo> DatabaseInfos { get; set; }
}
[Injectable(InstanceScope.Singleton)]
[ImplementPropertyChanged]
public class DatabaseViewModel : IDatabaseViewModel
{
private RelayCommand _buttonClicked;
private ILogger _logger;
public DatabaseViewModel()
{
_logger = ServiceLocator.Default.GetInstance<ILoggerFactory>().GetLogger(this);
DatabaseInfos = new ObservableCollection<DatabaseInfo>();
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + #"\CampusInstallerSettings.xml";
if (File.Exists(path))
{
try
{
var settings = SettingsReader.LoadSettings(path);
foreach (var db in settings.DatabaseSettings.DatabaseSettings)
{
var dbInfo = new DatabaseInfo();
if (db.ActionCode.Value == "Upgrade")
dbInfo.Action = Action.Upgrade;
else if (db.ActionCode.Value == "None")
dbInfo.Action = Action.None;
else if (db.ActionCode.Value == "Remove")
dbInfo.Action = Action.Uninstall;
else
dbInfo.Action = Action.None;
dbInfo.SqlServer = db.SQLServer.Value;
dbInfo.Database = db.Database.Value;
if (db.DBType.Value == "CampusVue")
dbInfo.DatabaseType = DatabaseType.CampusVue;
if (db.Connection.Value == "Integrated")
dbInfo.IntegratedSecurity = true;
DatabaseInfos.Add(dbInfo);
}
}
catch (Exception ex)
{
_logger.Error(ex);
throw new Exception("Could not load settings file. " + Environment.NewLine + ex.Message);
}
}
else
{
throw new Exception("Could not find settings file # " + path);
}
}
public ICommand CmdButtonClicked
{
get { return _buttonClicked ?? (_buttonClicked = new RelayCommand(ButtonClicked)); }
}
public ObservableCollection<DatabaseInfo> DatabaseInfos { get; set; }
private void ButtonClicked(object o)
{
_logger.Info(#"Test button clicked.");
var dbInfo = o as IDatabaseInfo;
if (dbInfo != null && !dbInfo.TestConnection())
{
MessageBox.Show("Couldn't connect", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
Model
public interface IDatabaseInfo
{
Action Action { get; set; }
string SqlServer { get; set; }
string Database { get; set; }
DatabaseType DatabaseType { get; set; }
bool IntegratedSecurity { get; set; }
string Username { get; set; }
string Password { get; set; }
string CurrentVersion { get; set; }
bool TestConnection();
}
[ImplementPropertyChanged]
public class DatabaseInfo : IDatabaseInfo
{
public Action Action { get; set; }
public string SqlServer { get; set; }
public string Database { get; set; }
public DatabaseType DatabaseType { get; set; }
public bool IntegratedSecurity { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string CurrentVersion { get; set; }
public bool TestConnection()
{
if (DbHelper.TestConnection(SqlServer, 1433, Database, Username, Password))
{
CurrentVersion = DbHelper.GetDbVersion(SqlServer, 1433, Database, Username, Password);
return true;
}
return false;
}
}
You're not updating the property.
public string CurrentVersion { get; set; }
is what you want to update. Because you're not calling OnPropertyChanged in the setter, the TextBox isn't going to update to a new version.
private string _currentVersion = string.Empty;
public string CurrentVersion
{
get { return _currentVersion };
set
{
_currentVersion = value;
OnPropertyChanged("CurrentVersion");
}
}
I would guess that your DatabaseInfo is your Model ;). This is where you might choose to make a small ViewModel wrapper class that returns/sets the updated values so you keep INPC out of your Model layer. Otherwise you can go with the code snippet above :)
EDIT
In that case, just spell correctly ;)
TextBox Text="{Binding CurrentVerion}"

WPF MVVM Binding to SelectedItem of ListBox within DataTemplate of Parent ListBox

I have a ListBox within a DataTemplate of another ListBox simplified to the following XAML
<ListBox ItemsSource="{Binding MovieList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ListBox ItemsSource="{Binding Cast}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedCastMember, Mode=TwoWay}"/>
<TextBlock Text="{Binding MovieName}"/>
<TextBlock Text=....../>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The Parent List is bound to an ObservableCollection of Movie with each Movie object in turn having an ObservableCollection of cast members bound to a list box. The ViewModel property and Class below
public const string MovieListPropertyName = "MovieList";
private ObservableCollection<Movie> _movieList;
public ObservableCollection<Movie> MovieList
{
get
{
return _movieList;
}
set
{
if (_movieList == value)
{
return;
}
RaisePropertyChanging(MovieListPropertyName);
_movieList = value;
RaisePropertyChanged(MovieListPropertyName);
}
}
public const string SelectedCastMemberPropertyName = "SelectedCastMember";
private MovieCastMember _selectedCastMember;
public MovieCastMember SelectedCastMember
{
get
{
return _selectedCastMember;
}
set
{
if (_selectedCastMember == value)
{
return;
}
RaisePropertyChanging(SelectedCastMemberPropertyName);
_selectedCastMember = value;
RaisePropertyChanged(SelectedCastMemberPropertyName);
}
}
With the Movie and MovieCastMember classes as follows
public class Movie
{
public int Id { get; set; }
public string Name { get; set; }
public int Year { get; set; }
public string Overview { get; set; }
public double VoteAverage { get; set; }
public ObservableCollection<MovieCastMember> Cast { get; set; }
public BitmapImage PosterImage { get; set; }
}
public class MovieCastMember
{
public int Id { get; set; }
public string Name { get; set; }
}
I want to select a cast member in any of the Movie Lists and bind the MovieCastMember object to a property in my ViewModel. My List boxes populates fine, I have tried various scenarios in XAML but the SelectedItem are not updating the property in the ViewModel. Any help would be appreciated.
That's because you're binding against Movie class in second listBox. There is no "SelectedCastMember". Move it to Movie class and it will work.

Resources