|
Let me clarify that a bit more. I'm currently binding my buttons to be enabled if my Stored settings bool value is true. Meaning in this case dbOpen. This is true if my database is loaded, false if it's closed. Now I have certain buttons I want to be enabled when this is true so I have the xml code like this:
IsEnabled="{Binding Source={x:Static prop:Settings.Default}, Path=dbOpen, Mode=TwoWay}"
Now I want certain buttons to be able to be disabled when the dbOpen is true so how do I flip the value in the xml statement? in c# it would be like :
If (Settings.Default.dbOpen) to check if it's not true you would If(!Settings.Default.dbopen)
modified 24-Jul-12 11:04am.
|
|
|
|
|
I have a set of NOT converters!
public class BooleanNotConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool bReturn = false;
int iValue;
try
{
if (Int32.TryParse(value.ToString(), out iValue))
{
bReturn = iValue != 0;
}
else
{
Boolean.TryParse(value.ToString(), out bReturn);
}
}
catch { bReturn = false; }
return !bReturn;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int iReturn = 0;
bool bReturn = false;
Boolean.TryParse(value.ToString(), out bReturn);
if (!bReturn)
{ iReturn = -1; }
return iReturn;
}
}
And the xaml
IsEnabled="{Binding IsLocked,Converter={StaticResource BooleanNotConverter}}"
And the App.xaml resource
<!--Converter Resource-->
<ResourceDictionary>
<converter:BooleanNotConverter x:Key="BooleanNotConverter"/>
</ResourceDictionary>
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
For some reason it's just not working as expected. The open database button is enabled by default this allows it to open a database. Once I load the database I set the dbOpen property to True which if this is working properly should disable the button since it inverts the True to a false. But it's not. The Button is enabled the entire time. Now I know dbOpen is true because my code to load the database will only run if(!System.Properties.Default.dbOpen), and while the button does not disable the code to open the database does not fire when clicked.
|
|
|
|
|
I know this is a really dumb suggestion but try it any way, presuming your converter is working as expected and have tested it on some other control, try changing your button from a Command"" to Click. I have found (twice) that for some reason the IsEnabled event is ignored if there is a command on the button (I have no idea why and don't have the motivation to chase it down as it happens only rarely).
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
I tried that and sadly it didn't change anything. One thing is possible I'm using the Actipro WPF Ribbon and it's the QAT open button, and backstage open buttons I'm trying to disable using this method. I don't know if by chance it's an issue with their control or not.
I can tell you that I have had success using a regular boolean converter to enable/disable the buttons on their ribbon, just not this inverted boolean converter.
|
|
|
|
|
Got it working!
Was a really stupid mistake I forgot to tell it the source of where my variable to tie it too was. Since it's a Property stored by the project I had to do the IsEnabled command as so:
IsEnabled="{Binding Source={x:Static prop:Settings.Default}, Path=dbOpen, Converter={StaticResource booleanNotConverter}}"
|
|
|
|
|
While your code works just fine... a more generic solution is to use a map converter. That way you don't need to keep writing these one-off converters that basically map one value to another. It's all in one converter and you just define the mapping in XAML.
|
|
|
|
|
Well don't just drop it as a comment, link please. I have about 8 converters so there is very limited rewriting.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
. Ok...
It's part of this project: http://wpfconverters.codeplex.com/[^]
Like I said, nothing wrong with your code. Just that this converter allowed me to get rid of about 73 others. You can map any value you can reference in XAML to any other value.
I use it for bool inversion... I also use it in a clever way to allow for triggering on empty collections. I map count=0 to true and then use the fallback value (for any other count) = false.
|
|
|
|
|
Thanks for that, something for the weekend experimentation!
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Still trying to grasp MVVM here. I have 4 controls I display from my main form.
Basically
<DockPanel LastChildFill="True">
<views:RibbonControl x:Name="mainRibbon" DockPanel.Dock="Top" />
<controls:StatusBarControl x:Name="mainStatusBar" DockPanel.Dock="Bottom" />
<controls:NavigationControl x:Name="mainNavigation"
DockPanel.Dock="Left"
Visibility="Hidden" />
<controls:DisplayContactsControl x:Name="mainContactsDisplay"
DockPanel.Dock="Left"
Visibility="Hidden" />
</DockPanel>
How can I update say messages for the status bar when things occur inside one of the other controls. Lets say On the RibbonControl there is a button to open the database. The code to open it would include references to displaying a message on the statusbar control that the form is loading and also update the title of the form to display the path of the database, name and title of the application. Now I can do this in regular WPF but their is code behind I'm trying to understand how to do this in MVVM.
modified 24-Jul-12 11:05am.
|
|
|
|
|
Most people doing this would use either a Messenger or a Mediator. These are common techniques that play nicely with MVVM.
|
|
|
|
|
Using Binding on your StatusBarControl will helps you.
Christian Amado
MCITP | MCTS | MOS | MTA
DCE 0★ 1★ 2★ 3★ 4★ 5★
Bronze level MVA
Olimpia ☆ ★★★
|
|
|
|
|
I'm not sure I understand. How would binding allow me to display something like, Management Mode - Contacts Manager - Editing Contacts? Or Management Mode - Contacts Manager - Create New Contact?
|
|
|
|
|
Bind the controls text property to a property (VMProp) in your VM, update VMProp with the status you need to display. That update is driven by the processes in the VM that are initiate by the user.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
I think I understand. I've noticed that MVVM is basically Bind everything lol.
|
|
|
|
|
Alisaunder wrote: MVVM is basically Bind everything
You have that absolutely correct. No more code to manipulate the UI directly, everything is in the VM, or converters etc
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Unless the OP is using a single VM, you have missed important information out. How is another unrelated control going to be updated? If you see my answer, you can see the common mechanisms which answers the question the OP actually asked.
|
|
|
|
|
I'm trying to add images to a column in a WPF datagrid which are read from a folder and not added as a static resource. The datagrid shows the path to the image in debug but the cell is empty.
XAML:
<DataGrid
Name="dgDevices"
AutoGenerateColumns="False"
CanUserAddRows="False"
ItemsSource="{Binding Scanners}"
SelectionChanged="dgDevices_SelectionChanged">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Device Image">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Name="imgScanner" Source="{Binding Path=ImageFile}" Height="100" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Details" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap">
<TextBlock Text="{Binding Path=FriendlyName}"/>
<Hyperlink NavigateUri="{Binding Path=URL}" RequestNavigate="Hyperlink_RequestNavigate">
<TextBlock Text="{Binding Path=URL}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Code that describes the objects for the datagrid:
public class Scanners : INotifyPropertyChanged
{
#region dongleNum related info
private UInt32 thisIPAddress;
public UInt32 IPAddress
{
get { return thisIPAddress; }
set { thisIPAddress = value; NotifyPropertyChanged("IPAddress"); }
}
private UInt64 thisMACAddress;
public UInt64 MACAddress
{
get { return thisMACAddress; }
set { thisMACAddress = value; NotifyPropertyChanged("MACAddress"); }
}
#endregion
#region Device related info
private string thisPortName;
public string PortName
{
get { return thisPortName; }
set { thisPortName = value; NotifyPropertyChanged("PortName"); }
}
#endregion
#region Driver related info
private string thisDeviceGuid;
public string DeviceGuid
{
get { return thisDeviceGuid; }
set { thisDeviceGuid = value; NotifyPropertyChanged("DeviceGuid"); }
}
#endregion
#region Presentation related info
private string thisImageFile;
public string ImageFile
{
get { return thisImageFile; }
set { thisImageFile = value; NotifyPropertyChanged("ImageFile"); }
}
private string thisFriendlyName;
public string FriendlyName
{
get { return thisFriendlyName; }
set { thisFriendlyName = value; NotifyPropertyChanged("FriendlyName"); }
}
private Uri thisURL;
public Uri URL
{
get { return thisURL; }
set { thisURL = value; NotifyPropertyChanged("URL"); }
}
#endregion
#region Constructors
public Scanners(string newFriendlyName, string newImageFile)
{
FriendlyName = newFriendlyName;
ImageFile = newImageFile;
}
public Scanners(sxWrapper.sxDeviceInfo DeviceInfo, string newFriendlyName, Uri newURL, string newImageFile, string newDeviceGuid)
{
MACAddress = DeviceInfo.MACAddress;
IPAddress = DeviceInfo.IPAddress;
PortName = DeviceInfo.PortName;
FriendlyName = newFriendlyName;
URL = newURL;
ImageFile = newImageFile;
DeviceGuid = newDeviceGuid;
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public class DeviceViewModel
{
private static ObservableCollection<Scanners> devices = null;
public static ObservableCollection<Scanners> Devices
{
get { return devices; }
set { devices = value; }
}
public DeviceViewModel()
{
this.Initialize();
}
private void Initialize()
{
Reset();
}
public static void Reset()
{
devices = new ObservableCollection<Scanners>();
}
public static void Add(Scanners Scanner)
{
devices.Add(Scanner);
}
}
The Path "ImageFile" in the XAML is a string which is the image's location. i.e. "Images\image1.bmp" The folder Images is located in the target folder containing bmp images. I can only display images in the cells if the images are added as resources in the project. I read a string from an INI class that describes the file name in the Images folder. There are other strings read from the INI file that appear in the other column. Only the images don't appear.
I don't what I am doing wrong, please help.
|
|
|
|
|
Try this (works on Silverlight too):
Create a converter class:
public sealed class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your XAML you must add a new resource and implement a convert in your image element inside the CellTemplate. Remember to add the extension namespace on your xaml file header.
<DataGrid
Name="dgDevices"
AutoGenerateColumns="False"
CanUserAddRows="False"
ItemsSource="{Binding Scanners}" SelectionChanged="dgDevices_SelectionChanged"> <DataGrid.Resources>
<extension:ImageConverter x:key="imgConverter" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Device Image">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Name="imgScanner" Source="{Binding Path=ImageFile,
Converter={StaticResource imgConverter}}" }" Height="100" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Details" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap">
<TextBlock Text="{Binding Path=FriendlyName}"/>
<Hyperlink NavigateUri="{Binding Path=URL}" RequestNavigate="Hyperlink_RequestNavigate">
<TextBlock Text="{Binding Path=URL}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Hope its helps
Christian Amado
MCITP | MCTS | MOS | MTA
DCE 0★ 1★ 2★ 3★ 4★ 5★
Bronze level MVA
Olimpia ☆ ★★★
|
|
|
|
|
I found the following as a source of help towards the solution:
public class ImageConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new BitmapImage(new Uri((string)value, UriKind.RelativeOrAbsolute));
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
The path to the image is Images\File.bmp. I tried changing the formatting of the string to create a proper Uri, but the image STILL does not appear in the cell
I found a solution which helped in better creating the path to be in the proper form of a Uri. What follows IS the solution:
public class ImageConverter : IValueConverter
{
public static BitmapImage CreateBMImage(string path)
{
BitmapImage bi = new BitmapImage();
try
{
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnDemand;
Uri baseUri = new Uri(Application.ResourceAssembly.Location);
bi.UriSource = new Uri(baseUri, path);
bi.EndInit();
}
catch
{
return null;
}
return bi;
}
public static Image CreateImage(string path)
{
Image img = new Image();
img.Source = CreateBMImage(path);
return img;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return CreateBMImage((string)value);
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
modified 23-Jul-12 10:34am.
|
|
|
|
|
The subject is somewhat of a misnomer, I have a category combobox that governs what defects are available, but that is it. The category information should not be saved. Easier to show than say:
Here is the relevant ViewModel:
public Category SelectedCategory
{
get { return _selectedCategory; }
set
{
if (value == null) return;
_selectedCategory = value;
OnPropertyChanged("SelectedCategory");
LoadDefects(_selectedCategory.id);
}
}
And the View:
<ComboBox
Grid.Row="2"
Grid.Column="1"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.AllCategories}"
DisplayMemberPath="name"
SelectedValuePath="id"
SelectedValue="{Binding SelectedItem.Defect.categoryId}"
SelectedItem="{Binding SelectedCategory}" />
<ComboBox
Grid.Row="3"
Grid.Column="1"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.AllDefects}"
DisplayMemberPath="name"
SelectedValuePath="id"
SelectedValue="{Binding SelectedItem.defectId}"/>
What is happening is that instead of just saving the SelectedItem (Observation class not shown) it is changing SelectedItem.Defect.CategoryId, which I don't want. I only want SelectedItem to be saved. Save routine is pretty simple:
ViewModel:
private void Save()
{
_con.SaveChanges();
}
Thoughts?
Cheers, --EA
|
|
|
|
|
I think you're confusing how the various ComboBox properties work:
DisplayMemberPath = Choose how to display the item, equivalent to making an ItemTemplate with a TextBlock that binds to said path.
SelectedValuePath = Use with the SelectedValue property when the items in the list aren't the same as the property you're binding to. For example, the items in your list may have a Name and an ID, so you set the SelectedValuePath to "ID" and bind SelectedValue to the "SelectedID" property of your model.
SelectedValue = See previous
SelectedItem = Use this when the items in your list are the actual items in your model. Do not combine it with SelectedValue or SelectedValuePath.
|
|
|
|
|
I am certainly confused about the various properties and what combination to use them in. I understand what you have written, though.
Ideally, I would just have SelectedCategory drive that combobox as I need the INotifyProperyChanged to work to populate the defect list. But I also need that combobox to have an initial value based on the currently selected item.
So how do I accomplish this? I need the selected value to be SelectedItem.Defect.CategoryId, but I also need to notify my viewmodel when particular property is changed. Which one needs to be removed and how do I still accomplish what I need?
|
|
|
|
|
Well, let's look at your particular case...
Since your model property seems to match the item in the list (Correct me if I'm wrong), you don't need to worry about the SelectedValue/Path properties. So take those out of your first combo, and that one should work just fine.
As for the second one, you want its ItemsSource to change based on the SelectedCategory, right? Whatever your LoadDefects function is modifying, that's what you want the second combo's ItemsSource to bind to.
You should also have a SelectedDefect property (Or something of the sort), that the second combobox should bind to in its SelectedItem property. The LoadDefects function can set the SelectedDefect to an initial guess when it updates the collection.
So in your case, I don't think you really need SelectedValue/Path at all.
|
|
|
|