Related to Resx Translation Helper, V.2.0 Remove Files Window
First, I implemented the changes suggested in my last question, then I changed my entire code base to support editing 1-N Resx files. So, this question covers the main window. In particular, I wasn't able to remove all the code from the code-behind, and I have not been able to find a way to do so.
IResxTranslationHelperWindow.cs:
public interface IResxTranslationHelperWindow
{
object DataContext { set; }
event EventHandler<DataGridCellEditEndingEventArgs> EndCellEdit;
void Show();
}
ResxTranslationHelper.xaml:
<Grid>
<Grid.Resources>
<converters:StringListContainsNullOrEmptyValues x:Key="ArrayContainsEmptyVals" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Content="{x:Static properties:Resources.FilePickerButton_AddFile}" Command="{Binding PickFile}" />
<Button Grid.Column="1" Content="{x:Static properties:Resources.FilePickerButton_RemoveFile}" Command="{Binding RemoveFile}" />
</Grid>
<DataGrid Name="GridDisplay" ItemsSource="{Binding ResxStrings, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}"
CanUserSortColumns="True" SelectionMode="Single" CellEditEnding="GridDisplay_CellEditEnding" Grid.Row="1"
CanUserDeleteRows="True" CanUserAddRows="True" ColumnWidth="300" AutoGenerateColumns="False" AddingNewItem="GridDisplay_AddingNewItem"
SourceUpdated="GridDisplay_SourceUpdated" TargetUpdated="GridDisplay_SourceUpdated">
<DataGrid.InputBindings>
<KeyBinding Key="Delete" Command="{Binding DeleteItem}" CommandParameter="{Binding ElementName=GridDisplay, Path=SelectedValue}" />
</DataGrid.InputBindings>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Values, Converter={StaticResource ArrayContainsEmptyVals}}" Value="true">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Name="MouseEnterRow">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color" To="LightGray" Duration="0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<RemoveStoryboard BeginStoryboardName="MouseEnterRow" />
</EventTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Key}" Header="{x:Static properties:Resources.DataGridHeader_Key_Header}" CanUserSort="True" />
</DataGrid.Columns>
</DataGrid>
</Grid>
ResxTranslationHelper.xaml.cs:
private void GridDisplay_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.EditAction == DataGridEditAction.Commit)
{
OnEndCellEdit(e);
}
}
public event EventHandler<DataGridCellEditEndingEventArgs> EndCellEdit;
protected virtual void OnEndCellEdit(DataGridCellEditEndingEventArgs e)
{
var handler = EndCellEdit;
if (handler != null)
{
handler(this, e);
}
}
private void RemoveColumns(DataGrid grid, int columnCount)
{
while (grid.Columns.Count > columnCount) // leave Keys column
{
grid.Columns.RemoveAt(columnCount);
}
}
private void GridDisplay_SourceUpdated(object sender, DataTransferEventArgs e)
{
var grid = (DataGrid) sender;
var source = (ObservableCollection<ResxValues>) grid.ItemsSource;
if (source.Count == 0 || source.First().Values.FirstOrDefault() == null)
{
RemoveColumns(grid, 1); // reset UI to startup
return;
}
RemoveColumns(grid, source.First().Values.Count()); // remove removed columns
// add added columns
for (var i = grid.Columns.Count - 1; i < source.First().Values.Count(); i++)
{
var column = new DataGridTextColumn
{
Binding = new Binding(string.Format("Values[{0}]", i)),
Header = Properties.Resources.DataGridHeader_Value_Header,
CanUserSort = true
};
grid.Columns.Add(column);
}
}
private void GridDisplay_AddingNewItem(object sender, AddingNewItemEventArgs e)
{
var grid = (DataGrid) sender;
var valCount = grid.Columns.Count - 1;
var arr = new string[valCount];
e.NewItem = new ResxValues(string.Empty, arr.Select(x => string.Empty).ToArray());
}
ResxTranslationHelperVM.cs:
private readonly IResxTranslationHelperWindow _window;
private readonly List<ResxFileComponents> _components;
// ReSharper disable once MemberCanBePrivate.Global
public ObservableCollection<ResxValues> ResxStrings { get; set; }
private ICommand _pickFile;
public ICommand PickFile
{
get
{
return _pickFile ?? (_pickFile = new RelayCommand
(
param =>
{
var filePaths = OpenFilePicker();
foreach (var filePath in filePaths.Where(filePath => !string.IsNullOrEmpty(filePath) && filePath.EndsWith(".resx") && _components.All(c => c.FilePath != filePath)))
{
_components.Add(new ResxFileComponents(filePath));
}
ProcessData();
}
));
}
}
private ICommand _removeFile;
public ICommand RemoveFile
{
get
{
return _removeFile ?? (_removeFile = new RelayCommand
(
param =>
{
var filePaths = OpenFileCloser();
foreach (var component in filePaths.Select(filePath => _components.FirstOrDefault(c => c.FilePath == filePath)).Where(component => component != null))
{
_components.Remove(component);
}
ProcessData();
}
));
}
}
private ICommand _deleteItem;
public ICommand DeleteItem
{
get
{
return _deleteItem ?? (_deleteItem = new RelayCommand
(
param => DeleteRow(ResxStrings.IndexOf((ResxValues) param))
));
}
}
public ResxTranslationHelperVM(IResxTranslationHelperWindow window)
{
_window = window;
_components = new List<ResxFileComponents>();
ResxStrings = new ObservableCollection<ResxValues>();
_window.EndCellEdit += EndCellEdit;
window.DataContext = this;
}
private IEnumerable<string> OpenFilePicker()
{
var filePicker = new OpenFileDialog
{
Multiselect = true,
SupportMultiDottedExtensions = true,
CheckPathExists = true,
Filter = @"Resx Files|*.resx",
CheckFileExists = true,
ValidateNames = true
};
filePicker.ShowDialog();
return filePicker.FileNames;
}
private IEnumerable<string> OpenFileCloser()
{
var filesSelected = new List<string>();
var thread = new Thread(() =>
{
var displayOpenFilesVM = new DisplayOpenFilesVM(
new DisplayOpenFiles(),
_components.Select(c => c.FilePath));
displayOpenFilesVM.Show();
displayOpenFilesVM.FilesSelected += (s, e) =>
{
filesSelected = e.ToList();
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
};
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return filesSelected;
}
private void EndCellEdit(object sender, DataGridCellEditEndingEventArgs e)
{
var columnIndex = e.Column.DisplayIndex;
var key = (string)e.Column.Header == Resources.DataGridHeader_Key_Header
? ((TextBox)e.EditingElement).Text
: ((ResxValues)e.Row.Item).Key;
if (columnIndex == 0)
{
if (string.IsNullOrEmpty(key) || ResxStrings.FirstOrDefault(s => s.Key == key) != null)
{
var message = string.IsNullOrEmpty(key)
? Resources.EndCellEdit_EmptyKeyError
: Resources.EndCellEdit_KeyAlreadyExists;
MessageBox.Show(message, Resources.EndCellEdit_AddValueError,
MessageBoxButtons.OK, MessageBoxIcon.Warning);
var newText = "String" + ResxStrings.Count(r => r.Key.StartsWith("String"));
while (ResxStrings.FirstOrDefault(r => r.Key == newText) != null)
{
newText += new Random().Next(0, 9);
}
((TextBox) e.EditingElement).Text = newText;
key = newText;
}
foreach (var c in _components)
{
c.AddNode(key, c.GetNodeValue(((ResxValues)e.Row.Item).Key));
c.RemoveNode(((ResxValues)e.Row.Item).Key);
c.Save();
}
}
else
{
_components.ElementAt(columnIndex - 1).RemoveNode(key);
_components.ElementAt(columnIndex - 1).AddNode(key, ((TextBox)e.EditingElement).Text);
_components.ElementAt(columnIndex - 1).Save();
}
}
private void DeleteRow(int index)
{
RemoveNode(ResxStrings[index].Key);
SaveFiles();
ResxStrings.RemoveAt(index);
}
private void SaveFiles()
{
foreach (var c in _components)
{
c.Save();
}
}
private void RemoveNode(string key)
{
foreach (var c in _components)
{
c.RemoveNode(key);
}
}
public void Load()
{
_window.Show();
}
private void ProcessData()
{
ResxStrings.Clear();
foreach (var c in _components.Where(c => _components.All(x => x.FilePath != c.FilePath)))
{
_components.Add(new ResxFileComponents(c.FilePath));
}
var keys = new List<string>();
keys = _components.Aggregate(keys,
(current, c) => current.Union(c.StringNodes.Select(n => n.FirstAttribute.Value)).ToList());
var values = keys.Select(key => _components.Select(c => c.GetNodeValue(key)).ToArray()).ToList();
for (var i = 0; i < keys.Count; i++)
{
ResxStrings.Add(new ResxValues(keys[i], values[i]));
}
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged("ResxStrings"); // trigger for UI to load new columns
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}