I have an entity that includes image that type byte[]. So the return type is byte[].I have to get image and show on a listbox.My codes are below.
public class KitapListesiControl extends SelectorComposer<Component> {
private static final long serialVersionUID = 1L;
private List<Kitaplar> kitapList = new ArrayList<Kitaplar>();
private ListModel<Kitaplar> kitapListModel;
#Wire
private Listbox kitapListBox;
#Wire
private Image kitapImageId = new Image();
KitapIslemleriDao kid = new KitapIslemleriDaoImpl();
#Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
kitapList = kid.findAllKitaplar();
kitapListModel = new ListModelList<Kitaplar>(kitapList);
kitapListBox.setModel(kitapListModel);
kitapImageId.setId("kitapImageId" + kitapList.get(0).getKitapId());
}
My listbox that want to display image on a cell is below
<listbox id="kitapListBox" emptyMessage="Kayıt yok!"
mold="paging" pageSize="5" width="970px" checkmark="true"
apply="com.mesutemre.kitapIslemleri.KitapListesiControl"
model="${win$composer.kitapListModel}">
<listhead>
<listheader width="32px" />
<listheader label="Kitap Adı" align="center"
sort="auto(kitapad)" width="190px" />
<listheader label="Yazar Adı" align="center"
sort="auto(yazarad)" width="190px" />
<listheader label="Kitap Tür" align="center"
width="190px" sort="auto(kitaptur)" />
<listheader label="Kitap Durum" align="center"
sort="auto(kitapdurum)" width="190px" />
<listheader label="Kitap Resmi" align="center"
width="190px" />
</listhead>
<template name="model">
<listitem>
<listcell />
<listcell label="${each.kitapad}" />
<listcell label="${each.yazarad}" />
<listcell label="${each.kitaptur}" />
<listcell label="${each.kitapdurum}" />
<listcell>
<image
src="#load(each.kitapimage) #converter('com.mesutemre.converter.ImageToZkImageConverter')" />
</listcell>
</listitem>
</template>
</listbox>
Zul:
<image content="#load(each.image) #converter('be.chillworld.web.vm.util.ImageToZkImageConverter')" />
ImageToZkImageConverter.class :
package be.chillworld.web.vm.util;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zkoss.bind.BindContext;
import org.zkoss.bind.Converter;
import org.zkoss.image.AImage;
import org.zkoss.zul.Image;
public class ImageToZkImageConverter implements Converter<AImage, byte[], Image> {
private Log logger = LogFactory.getLog(ImageToZkImageConverter.class);
#Override
public byte[] coerceToBean(AImage compAttr, Image component, BindContext ctx) {
logger.debug("Converting the image");
return compAttr.getByteData();
}
#Override
public AImage coerceToUi(byte[] beanProp, Image component, BindContext ctx) {
try {
if (beanProp != null && beanProp.length > 0) {
AImage im = new AImage("", beanProp);
component.setContent(im);
return im;
}
logger.debug("Return null => image is empty");
return null;
} catch (IOException e) {
logger.error("Error occured, returning null", e);
return null;
}
}
}
source : http://forum.zkoss.org/question/95645/how-to-get-image-lob-attribute-from-database-into-listcell/#95651
For MVC you can do the following :
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
....
<image content="${c:new2('org.zkoss.image.AImage','',each.image)}" />
....
Related
I'm really in trouble!
I need some brilliant idea to get a jsf page in which 7 charts slide one after another every 20 seconds and I need these charts are
refreshed every 30 seconds.
I tried some solutions with bad results:
1.Slideshow + Poll
<h:form>
<div align="center">
<p:panel id="chartcontainer" style="border: none;">
<p:imageSwitch style="width: 100%"
id="slider"
widgetVar="chartSlideShow"
effect="turnDown"
slideshowSpeed="5000">
<ui:include src="/charts/chart1.xhtml"/>
<ui:include src="/charts/chart2.xhtml"/>
<ui:include src="/charts/chart3.xhtml"/>
<ui:include src="/charts/chart4.xhtml"/>
<ui:include src="/charts/chart5.xhtml"/>
<ui:include src="/charts/chart6.xhtml"/>
<ui:include src="/charts/chart7.xhtml"/>
</p:imageSwitch>
</p:panel>
<p:poll widgetVar="pollWidget"
update="#form"
interval="30"
oncomplete="PF('btnPlayWidget').disable();"/>
</div>
</h:form>
This solution's problem is that the slideshow and poll are not synchronized so when I'm watching the chart3, for example, and poll is executed I don't expect the change and the slideshow restart from the first chart. This is really annoying!
2.Poll by itself
<h:form>
<div align='center'>
<p:panel id="chartcontainer" style="border: none;">
<ui:include src="#{slideView.slide}"/>
</p:panel>
<p:poll widgetVar="pollWidget"
update="#form"
interval="20"
listener="#{slideView.next()}"
oncomplete="PF('btnPlayWidget').disable();"/>
</div>
</h:form>
Here's my slideView's bean:
package com.tvop.beans;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class SlideView implements Serializable {
private int index = 0;
String slide = "/charts/chart1.xhtml";
private final String[] slides = new String[] {
"/charts/chart1.xhtml",
"/charts/chart2.xhtml",
"/charts/chart3.xhtml",
"/charts/chart4.xhtml",
"/charts/chart5.xhtml",
"/charts/chart6.xhtml",
"/charts/chart7.xhtml"
};
public String getSlide() {
return slide;
}
public void setSlide(String slide) {
this.slide = slide;
}
public String next() {
index %= slides.length;
this.slide = slides[index];
index++;
return slide;
}
}
This solution runs more or less but the poll's interval is not exact, especially at the beginning, as soon as the page is loaded.
The first change between the first and the second chart happens after 30/35 seconds and not 20, as setted in the poll's interval.
I really need some good idea, I don't want to be fired.
Thank you all my friends!
Solved!
The solution is to use the poll component by its own; updating the poll every 20 seconds it emulates the imageSwitch behavior! It's important to modify the java bean too to make it work.
Following the solution:
xhtml poll:
<div align='center'>
<p:panel id="chartcontainer" style="border: none;">
<p:panel style="border: 0px">
<ui:include src="#{slideView.slide}" />
</p:panel>
</p:panel>
<p:poll widgetVar="pollWidget"
update="chartcontainer"
interval="20"
listener="#{slideView.next()}"
oncomplete="PF('btnPlayWidget').disable();"/>
</div>
slideview bean:
package com.tvop.beans;
import com.tvop.exceptions.DMLException;
import com.tvop.persistence.TkResourceJPA;
import com.tvop.persistence.dbentities.TkResource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
#ManagedBean
#ViewScoped
public class SlideView implements Serializable {
private SessionManager sessionManager = null;
private List<TkResource> resources;
private int index;
private String slide;
private final List<String> slides = new ArrayList<>();
public SlideView() {
// Ottengo i privilegi sui chart tramite il bean sessionManager
FacesContext ctx = FacesContext.getCurrentInstance();
ExternalContext extCtx = ctx.getExternalContext();
Map<String, Object> sessionMap = extCtx.getSessionMap();
sessionManager = (SessionManager) sessionMap.get("sessionManager");
try {
resources = TkResourceJPA.getCharts();
} catch (DMLException ex) {
ex.printStackTrace();
}
SessionPrivileges privileges = sessionManager.getSessionPrivileges();
for(TkResource resource : resources) {
// Se è definito un privilegio controllo che privilegio è
if (privileges.contains(resource.getResourceid())) {
boolean currentResRendered = privileges.getRendered(resource.getResourceid());
if(currentResRendered){
slides.add(resource.getUrl());
}
}
}
index = 0;
slide = slides.get(index);
}
public String getSlide() {
return slide;
}
public void setSlide(String slide) {
this.slide = slide;
}
public String next() {
index %= slides.size();
if (index == slides.size() - 1) {
index = -1;
}
index++;
this.slide = slides.get(index);
return slide;
}
public String previous() {
if (index == 0) {
index = slides.size();
}
--index;
this.slide = slides.get(index);
return slide;
}
}
Enjoy :)
I have a splitview,that contains StackPanels,I want to define a style which modify the color when hover in each of those StackPanels,is this possible?
this is my code:
<StackPanel Orientation="Horizontal" Style="{Binding Source={StaticResource HyperlinkPointerOverForegroundThemeBrush}}" >
<Button x:Name="MenuButton1"
Width="50" Height="50" Background="Transparent">
<Button.Content>
<Image Source="images/chercher.png"></Image>
</Button.Content>
</Button>
<TextBlock Text="Resultats" FontSize="18" VerticalAlignment="Center" Foreground="#727271"/>
</StackPanel>
and I have defined the styles like that:
<SolidColorBrush x:Key="HyperlinkButtonBackgroundThemeBrush" Color="#e6e6e6" />
<SolidColorBrush x:Key="HyperlinkButtonBorderThemeBrush" Color="#e6e6e6" />
<SolidColorBrush x:Key="HyperlinkDisabledThemeBrush" Color="#e6e6e6" />
<SolidColorBrush x:Key="HyperlinkForegroundThemeBrush" Color="#e6e6e6" />
<SolidColorBrush x:Key="HyperlinkPointerOverForegroundThemeBrush" Color="#e6e6e6" />
<SolidColorBrush x:Key="HyperlinkPressedForegroundThemeBrush" Color="#e6e6e6" />
but when I hover,none of the StackPanels Foreground have changed
thanks for help
I don't think you can do that only with style. But, if you really want to use a style, you can combine with a behavior.
The behavior can use PointerEntered and PointerExited events to set a new Background. Next, the behavior can be setted in your style.
In order to implements this type of solution, you have to add the Behaviors SDK extensions (go to add references > Universal Windows > Extensions).
The definition of the behavior (for example) :
public class HoverBehavior : DependencyObject, Microsoft.Xaml.Interactivity.IBehavior
{
private Panel _associatedObject;
private Brush _baseBrush;
public Brush HoverBrush
{
get { return (Brush)GetValue(HoverBrushProperty); }
set { SetValue(HoverBrushProperty, value); }
}
public static readonly DependencyProperty HoverBrushProperty =
DependencyProperty.Register("HoverBrush", typeof(Brush), typeof(HoverBehavior), new PropertyMetadata(null));
public Brush DefaultBrush
{
get { return (Brush)GetValue(DefaultBrushProperty); }
set { SetValue(DefaultBrushProperty, value); }
}
public static readonly DependencyProperty DefaultBrushProperty =
DependencyProperty.Register("DefaultBrush", typeof(Brush), typeof(HoverBehavior), new PropertyMetadata(null));
public DependencyObject AssociatedObject
{
get { return _associatedObject; }
}
public void Attach(DependencyObject associatedObject)
{
_associatedObject = associatedObject as Panel;
if (_associatedObject != null)
{
_baseBrush = _associatedObject.Background;
if (_associatedObject != null)
{
_associatedObject.PointerEntered += _associatedObject_PointerEntered;
_associatedObject.PointerExited += _associatedObject_PointerExited;
}
}
}
private void _associatedObject_PointerExited(object sender, PointerRoutedEventArgs e)
{
if (_associatedObject != null)
_associatedObject.Background = HoverBrush;
}
private void _associatedObject_PointerEntered(object sender, PointerRoutedEventArgs e)
{
if (_associatedObject != null)
_associatedObject.Background = DefaultBrush ?? _baseBrush;
}
public void Detach()
{
_associatedObject.PointerEntered -= _associatedObject_PointerEntered;
_associatedObject.PointerExited -= _associatedObject_PointerExited;
}
}
In the xaml, you have to add a "using" for the Interactivity namespace :
<Page xmlns:i="using:Microsoft.Xaml.Interactivity" />
And, you can define a new style (named SpStyle for example) :
<Style TargetType="StackPanel"
x:Key="SpStyle">
<Setter Property="i:Interaction.Behaviors">
<Setter.Value>
<i:BehaviorCollection>
<local:HoverBehavior DefaultBrush="Yellow"
HoverBrush="Green" />
</i:BehaviorCollection>
</Setter.Value>
</Setter>
</Style>
The HoverBrush property of the behavior is the color of the StackPanel when the mouse pointer is hover the StackPanel, and DefaultBrush (can be unset) is the color of the StackPanel when the mouse pointer is not hover the StackPanel.
Have a nice day. I don't know if i'm wrong about this. I have a form in my xhtml like this:
<p:outputLabel value="Número de pasajeros" />:
<p:inputText value="#{vueloMB.instancia.numPasajeros}" maxlength="3" >
</p:inputText>
<br />
<p:outputLabel value="Hora de salida" />:
<p:calendar value="#{vueloMB.instancia.fechaHoraSalida}" navigator="true"
mode="popup" pattern="dd/MM/yyyy HH:mm" />
<br />
<p:outputLabel value="Avión" />:
<p:selectOneMenu value="#{vueloMB.instancia.avion}" >
<f:selectItems value="#{vueloMB.aviones}" var="avi"
itemLabel="#{avi.modelo}" itemValue="#{avi}" />
</p:selectOneMenu>
<br />
<p:outputLabel value="Pais de salida" />:
<p:selectOneMenu value="#{vueloMB.instancia.paisSalida}" converter="omnifaces.SelectItemsConverter" >
<f:selectItems value="#{vueloMB.paises}" var="pai"
itemLabel="#{pai.nombre}" itemValue="#{pai}" />
<f:param name="tipoPais" value="S"></f:param>
<p:ajax update="ciusal" listener="#{vueloMB.cargarListaCiudades}" process="#this" >
</p:ajax>
</p:selectOneMenu>
<br />
<p:outputLabel value="Ciudad de salida" />:
<p:selectOneMenu value="#{vueloMB.instancia.ciudadSalida}" converter="omnifaces.SelectItemsConverter"
id="ciusal" disabled="#{vueloMB.instancia.paisSalida==null}" >
<f:selectItems value="#{vueloMB.ciudadesSalida}" var="ciu"
itemLabel="#{ciu.nombre}" itemValue="#{ciu}" />
</p:selectOneMenu>
<br />
<p:commandButton value="Guardar" rendered="#{vueloMB.instancia.id == null}" action="#{vueloMB.guardar()}" process="#form" ajax="true" />
</h:form>
The dropdown labeled "Ciudad de salida" refreshes another dropdown after i choose a country here, updates the list that feeds the second dropdown and it works fine. The problem is when i press the "Guardar" button to save the entity (vueloMB.instancia is my entity) with JPA, because it doesn't do anything.
So, i added the attribute immediate="true" to the button, it calls the ManagedBean method, but when i see the entity, only the field vueloMB.instancia.paisSalida isn't null, even if i fill all the fields. Because of that, i assumed that, because the dropdown calls an MB method because it refresh the second dropdown, it's value is refreshed on the MB. Based on that, i modified the first field like this:
<p:inputText value="#{vueloMB.instancia.numPasajeros}" maxlength="3" >
<p:ajax />
</p:inputText>
I added the ajax tag to my inputText. After doing that, i press the "Guardar" button and the field that i've modified (Número de pasajeros) now it carries the value on vueloMB.instancia.numPasajeros.
So, if i add to all my fields, when i press the submit button it will work, it will save the entity without problems and all the fields will travel to the managed bean, but is necessary to do that with every field? There's no automatic way JSF does this? Or i have something wrong with my code?
EDIT: Here is the code of the managed bean. A CDI Managed Bean with #ConversationScoped:
package com.saplic.fut.beans;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import com.saplic.fut.daos.VueloDAO;
import com.saplic.fut.entity.Avion;
import com.saplic.fut.entity.Ciudad;
import com.saplic.fut.entity.Pais;
import com.saplic.fut.entity.Vuelo;
#Named("vueloMB")
#ConversationScoped
public class VueloManagedBean implements Serializable {
private static final long serialVersionUID = -203436251219946811L;
#Inject
private VueloDAO vueloDAO;
#Inject
private Conversation conversation;
#PostConstruct
public void iniciarConversacion() {
if(conversation.isTransient())
conversation.begin();
}
public void finalizarConversacion() {
if(!conversation.isTransient())
conversation.end();
}
private Vuelo instancia;
private List<Vuelo> vuelos;
private List<Avion> aviones = new ArrayList<Avion>();
private List<Pais> paises = new ArrayList<Pais>();
private List<Ciudad> ciudadesSalida = new ArrayList<Ciudad>();
private List<Ciudad> ciudadesAterrizaje = new ArrayList<Ciudad>();
private Integer idVuelo;
public String cargarLista() {
iniciarConversacion();
vuelos = vueloDAO.cargarVuelos();
return "/vuelos/lista";
}
public void cargarListaCiudades() {
String tipoLista = FacesContext.getCurrentInstance().
getExternalContext().getRequestParameterMap().get("tipoPais");
if(tipoLista.equalsIgnoreCase("S"))
setCiudadesSalida(vueloDAO.cargarCiudades(getInstancia().getPaisSalida()));
if(tipoLista.equalsIgnoreCase("A"))
setCiudadesAterrizaje(vueloDAO.cargarCiudades(getInstancia().getPaisAterrizaje()));
}
public String cargarDetalle() {
Vuelo fltVuelo = new Vuelo();
fltVuelo.setId(getIdVuelo());
instancia = vueloDAO.cargarDetalle(fltVuelo);
if(instancia == null)
setInstancia(new Vuelo());
//Cargamos lista de aviones para combo
setAviones(vueloDAO.cargarAviones());
setPaises(vueloDAO.cargarPaises());
return "/vuelos/detalle";
}
public String guardar() {
vueloDAO.guardar(instancia);
finalizarConversacion();
return cargarLista();
}
public String actualizar() {
vueloDAO.actualizar(instancia);
finalizarConversacion();
return cargarLista();
}
public String eliminar() {
vueloDAO.eliminar(instancia);
finalizarConversacion();
return cargarLista();
}
public Vuelo getInstancia() {
return instancia;
}
public void setInstancia(Vuelo instancia) {
this.instancia = instancia;
}
public List<Vuelo> getVuelos() {
return vuelos;
}
public void setVuelos(List<Vuelo> vuelos) {
this.vuelos = vuelos;
}
public Integer getIdVuelo() {
return idVuelo;
}
public void setIdVuelo(Integer idVuelo) {
this.idVuelo = idVuelo;
}
public List<Avion> getAviones() {
return aviones;
}
public void setAviones(List<Avion> aviones) {
this.aviones = aviones;
}
public List<Pais> getPaises() {
return paises;
}
public void setPaises(List<Pais> paises) {
this.paises = paises;
}
public List<Ciudad> getCiudadesSalida() {
return ciudadesSalida;
}
public void setCiudadesSalida(List<Ciudad> ciudadesSalida) {
this.ciudadesSalida = ciudadesSalida;
}
public List<Ciudad> getCiudadesAterrizaje() {
return ciudadesAterrizaje;
}
public void setCiudadesAterrizaje(List<Ciudad> ciudadesAterrizaje) {
this.ciudadesAterrizaje = ciudadesAterrizaje;
}
}
Regards.
Your entities must implements the method equals() hashCode() and toString as specified in the omnifaces showcase. I can't help you much more than that since I'm not familiar with omnifaces and the ConversationScope. I think it's because the two objects are not at the same place in memory so when you use equals the result is false. In the case of omnifaces I read it uses toString() to see if two objects are equal so if the method is not reimplemented you will have different results.
In other words you have null values because when the value as string comes back from the form it cannot be converted back to the original object. I'd appreciate if someone could attest this as I'm not 100% positive that's what is happening.
I am writing a simple CRUD application (I'm using JSF 2.0 and mybatis3 on an oracle database) and so far I encountered an error while displaying the data (in a ) that I queried. I know this is really messy but I'd love some advice to what I am doing wrong. Thanks !
EDIT 1: I also made a plain java project and tested out the functions to see if they retunr anything or not and it worked properly...
EDIt 2: Log here, no errors tho http://pastebin.com/gYyqBX7W
Here is the a part from UserMapper.xml:
<resultMap id="result" type="User">
<result property="username" column="USER_ID" />
<result property="userDescription" column="USER_DESC" />
<result property="password" column="USER_PWD" />
</resultMap>
<select id="getAllUsers" resultMap="result">
SELECT USER_ID as username, USER_DESC as userDescription,
USER_PWD as password FROM SSLS_GUI.USERS
</select>
the UserMapper.java file:
package com.mybatis.mappers;
import java.util.List;
import com.optsol.beans.User;
public interface UserMapper {
public List<User> getAllUsers();
}
The part from UserService.java that intrests me:
#ManagedBean(name = "userServices")
public class UserService {
public List<User> getAllUsers() {
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()
.openSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
return userMapper.getAllUsers();
} finally {
sqlSession.close();
}
}
The MyBatisUtil.java file where I get the factory instance from:
package com.mybatis.service;
import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisUtil {
private static SqlSessionFactory factory;
private MyBatisUtil() {
}
static {
String resource = "com/mybatis/config/mybatis-config.xml";
Reader reader = null;
try {
reader = Resources.getResourceAsReader(resource);
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
factory = new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSessionFactory getSqlSessionFactory() {
return factory;
}
}
The User.java (without the getters and setters):
package com.optsol.beans;
import javax.faces.bean.ManagedBean;
#ManagedBean(name = "user")
public class User {
private String username;
private String userDescription;
private String password;
}
The index.xhtml where I try to display the data from the database:
<h:dataTable value="#{userService.getAllUsers}" var="u">
<h:column>
<f:facet name="header">
USERNAME
</f:facet>
#{u.username}
</h:column>
<h:column>
<f:facet name="header">
USER DESCRIPTION
</f:facet>
#{u.userDescription}
</h:column>
<h:column>
<f:facet name="header">
PASSWORD(HASHED)
</f:facet>
#{u.password}
</h:column>
</h:dataTable>
The name of the properties does not match the name of the columns.
<resultMap id="result" type="User">
<result property="username" column="USER_ID" />
<result property="userDescription" column="USER_DESC" />
<result property="password" column="USER_PWD" />
</resultMap>
Remove the alias in the SQL.
<select id="getAllUsers" resultMap="result">
SELECT USER_ID,
USER_DESC,
USER_PWD
FROM SSLS_GUI.USERS
</select>
Can anyone give out the best practices for playing around with controls at runtime such as creating a new view,adding views inside a view,adding controls to containers using MVVM pattern without breaking mvvm pattern??
I am using MVVMlight toolkit..
please help me out in this regard..
Thanks in advance......
This post discusses the strategies for creating views (dialogs) from your view models.
Edit:
From your comment I take it that you got an user interface that has an add and delete button. The add button should add an item (type ?) to a ItemsControl ... hope that's correct.
So, how would I do this, well I would create a view model that has an ObservableCollecion<ItemViewModel>. The ItemViewModle is the view mode that represents the item that should be added to the ItemsControl (so in your case the view model backing your "rangeView").
Then I would add two commands that handle the addition and deletion of items. Both commands just add/remove ItemViewModels from your collection.
To show the items in the view I would bind the ItemControl.ItemsSource property to the collection in your main view model (i.e. the one holding the ItemViewModel instances). The I would supply an ItemTemplate to render the items on the screen.
Ok, here is an example of what I think you are trying to do (at least conceptionally). Complete Source Code here. In the example I used a ListBox as it allows me easily to determine which item is selected, this depends on your szenario. Also note that you have complete freedom to customize the Template, the ItemPanelTemplate, and DataTemplate to fit your needs. You can even use this approacht to create PanoramaPages in WP7!
2nd edit: ItemsControl does not have a SelectedItem property. To get to it you have to use a control inheriting from Selector (e.g. a ListBox as I did) or you can use the Selector directly.
ItemViewModel:
public class ItemViewModel : ViewModelBase
{
#region [Name]
public const string NamePropertyName = "Name";
private string _name = null;
public string Name {
get {
return _name;
}
set {
if (_name == value) {
return;
}
var oldValue = _name;
_name = value;
RaisePropertyChanged(NamePropertyName);
}
}
#endregion
}
MainViewModel:
public class MainViewModel : ViewModelBase
{
public MainViewModel() {
if (IsInDesignMode) {
this.Items = new ObservableCollection<ItemViewModel>(Enumerable.Range(0, 10).Select((x, i) => new ItemViewModel() { Name = "Design Time Item " + i }));
} else {
// Code runs "for real"
}
}
#region [AddCommand]
private RelayCommand _addCommand;
public RelayCommand AddCommand {
get {
return _addCommand ?? (_addCommand = new RelayCommand(
() => {
this.Items.Add(new ItemViewModel() { Name = "New item - " + DateTime.Now });
}
));
}
}
#endregion
#region [DeleteCommand]
private RelayCommand _deleteCommand;
public RelayCommand DeleteCommand {
get {
return _deleteCommand ?? (_deleteCommand = new RelayCommand(
() => {
this.Items.Remove(this.SelectedItem);
},
() => { return this.SelectedItem != null; }
));
}
}
#endregion
#region [Items]
public const string ItemsPropertyName = "Items";
private ObservableCollection<ItemViewModel> _items = new ObservableCollection<ItemViewModel>();
public ObservableCollection<ItemViewModel> Items {
get {
return _items;
}
set {
if (_items == value) {
return;
}
var oldValue = _items;
_items = value;
RaisePropertyChanged(ItemsPropertyName);
}
}
#endregion
#region [SelectedItem]
public const string SelectedItemPropertyName = "SelectedItem";
private ItemViewModel _selectedItem = null;
public ItemViewModel SelectedItem {
get {
return _selectedItem;
}
set {
if (_selectedItem == value) {
return;
}
var oldValue = _selectedItem;
_selectedItem = value;
RaisePropertyChanged(SelectedItemPropertyName);
// important in SL to notify command that can execute has changed !
this.DeleteCommand.RaiseCanExecuteChanged();
}
}
#endregion
}
MainPage.xaml
<UserControl
x:Class="MvvmLight1.MainPage"
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"
mc:Ignorable="d"
Height="300"
Width="300"
DataContext="{Binding Main, Source={StaticResource Locator}}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- we are dealing with ItemViewModels now -->
<Border BorderThickness="0,0,0,1" BorderBrush="Gray" Padding="10,5">
<TextBlock Text="{Binding Name}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Margin="5,10" Content="Add" Command="{Binding AddCommand}" Width="70"/>
<Button Margin="5,10" Content="Delete" Command="{Binding DeleteCommand}" Width="70"/>
</StackPanel>
</Grid>
</UserControl>