KryptonSaveFileDialog
Overview
The KryptonSaveFileDialog class provides a Krypton-themed wrapper around the standard Windows SaveFileDialog. It inherits from FileSaveDialogWrapper and implements IDisposable to provide enhanced appearance, consistent theming, and improved visual integration while maintaining full compatibility with the standard SaveFileDialog functionality.
Class Hierarchy
System.Object
└── System.MarshalByRefObject
└── System.ComponentModel.Component
└── System.Windows.Forms.CommonDialog
└── System.Windows.Forms.FileDialog
└── System.Windows.Forms.SaveFileDialog
└── Krypton.Toolkit.FileSaveDialogWrapper
└── Krypton.Toolkit.KryptonSaveFileDialog
Constructor and Initialization
public KryptonSaveFileDialog()
The constructor initializes enhanced features:
- Internal
SaveFileDialog: Creates and manages the containedSaveFileDialoginstance - Theme Integration: Automatic Krypton theming through FileSaveDialogWrapper
- Full Compatibility: All standard
SaveFileDialogproperties and methods are proxied
Key Properties
SaveFileDialog-Specific Properties
CreatePrompt Property
[DefaultValue(false)]
public bool CreatePrompt { get; set; }
- Purpose: Prompts user for permission to create a file if the specified file doesn't exist
- Category: Behavior
- Default Value: false
- Usage: When you want to confirm file creation before proceeding
OverwritePrompt Property
[DefaultValue(true)]
public bool OverwritePrompt { get; set; }
- Purpose: Displays a warning if the user specifies a file name that already exists
- Category: Behavior
- Default Value: true
- Usage: Prevents accidental overwriting of existing files
CheckWriteAccess Property (NET8_0_OR_GREATER)
[DefaultValue(true)]
public bool CheckWriteAccess { get; set; }
- Purpose: Verifies if the creation of the specified file will be successful
- Category: Behavior
- Default Value: true
- Availability: .NET 8.0 and later
- Usage: Validates write permissions before showing the dialog
Standard FileDialog Properties
All standard FileDialog properties are fully supported:
// File and path properties
public string FileName { get; set; }
public string[] FileNames { get; }
public string DefaultExt { get; set; }
public string InitialDirectory { get; set; }
// Filter and validation
public string Filter { get; set; }
public int FilterIndex { get; set; }
public bool ValidateNames { get; set; }
public bool CheckFileExists { get; set; }
public bool CheckPathExists { get; set; }
// Behavior options
public bool AddExtension { get; set; }
public bool DereferenceLinks { get; set; }
public bool RestoreDirectory { get; set; }
public bool SupportMultiDottedExtensions { get; set; }
// Appearance
public string Title { get; set; }
// .NET 8.0+ features
public Guid? ClientGuid { get; set; }
public FileDialogCustomPlacesCollection CustomPlaces { get; }
Key Methods
OpenFile Method
public Stream OpenFile()
- Purpose: Opens the file selected by the user with read-only permission
- Returns: Stream for the selected file
- Exception: ArgumentNullException if file name is null
- Usage: Direct file access after successful dialog result
ShowActualDialog Method
protected override DialogResult ShowActualDialog(IWin32Window? owner)
- Purpose: Internal method that displays the themed dialog
- Returns: DialogResult indicating user's choice
- Implementation: Delegates to the internal
SaveFileDialogwith Krypton theming
Standard Methods
public override void Reset() // Resets all properties to defaults
public override string ToString() // Returns string representation
public void Dispose() // Cleans up resources
Events
FileOk Event
public override event CancelEventHandler? FileOk
- Purpose: Occurs when the user clicks Save in the dialog
- Type: CancelEventHandler
- Usage: Validate file selection or cancel the operation
Advanced Usage Patterns
Basic File Save
public void SaveDocument()
{
using var saveDialog = new KryptonSaveFileDialog
{
Title = "Save Document",
Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*",
FilterIndex = 1,
DefaultExt = "txt",
OverwritePrompt = true,
CheckWriteAccess = true
};
if (saveDialog.ShowDialog() == DialogResult.OK)
{
// Save the file
File.WriteAllText(saveDialog.FileName, documentContent);
}
}
Advanced Save with Validation
public class DocumentSaver
{
public bool SaveDocument(string content, string defaultName)
{
using var saveDialog = new KryptonSaveFileDialog
{
Title = "Save Document",
FileName = defaultName,
Filter = "Word Documents (*.docx)|*.docx|Rich Text (*.rtf)|*.rtf|Text Files (*.txt)|*.txt",
FilterIndex = 1,
DefaultExt = "docx",
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
OverwritePrompt = true,
CreatePrompt = false,
CheckWriteAccess = true
};
// Add custom places for quick access
saveDialog.CustomPlaces.Add(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
saveDialog.CustomPlaces.Add(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
// Handle validation
saveDialog.FileOk += OnFileOk;
try
{
if (saveDialog.ShowDialog() == DialogResult.OK)
{
return SaveToFile(saveDialog.FileName, content, saveDialog.FilterIndex);
}
}
finally
{
saveDialog.FileOk -= OnFileOk;
}
return false;
}
private void OnFileOk(object? sender, CancelEventArgs e)
{
if (sender is KryptonSaveFileDialog dialog)
{
// Validate file extension
string extension = Path.GetExtension(dialog.FileName).ToLower();
string expectedExt = GetExpectedExtension(dialog.FilterIndex);
if (extension != expectedExt)
{
MessageBox.Show($"Please use the {expectedExt} extension.", "Invalid Extension",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
e.Cancel = true;
}
}
}
private string GetExpectedExtension(int filterIndex)
{
return filterIndex switch
{
1 => ".docx",
2 => ".rtf",
3 => ".txt",
_ => ".txt"
};
}
private bool SaveToFile(string fileName, string content, int filterIndex)
{
try
{
switch (filterIndex)
{
case 1: // Word document
return SaveAsWordDocument(fileName, content);
case 2: // Rich text
return SaveAsRichText(fileName, content);
case 3: // Plain text
File.WriteAllText(fileName, content);
return true;
default:
return false;
}
}
catch (Exception ex)
{
MessageBox.Show($"Error saving file: {ex.Message}", "Save Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
}
Multi-Format Export
public class ExportManager
{
public void ExportData(DataTable data)
{
using var saveDialog = new KryptonSaveFileDialog
{
Title = "Export Data",
Filter = "Excel Files (*.xlsx)|*.xlsx|CSV Files (*.csv)|*.csv|XML Files (*.xml)|*.xml",
FilterIndex = 1,
DefaultExt = "xlsx",
InitialDirectory = GetLastExportDirectory(),
OverwritePrompt = true
};
if (saveDialog.ShowDialog() == DialogResult.OK)
{
ExportToFormat(data, saveDialog.FileName, saveDialog.FilterIndex);
SaveLastExportDirectory(Path.GetDirectoryName(saveDialog.FileName));
}
}
private void ExportToFormat(DataTable data, string fileName, int formatIndex)
{
switch (formatIndex)
{
case 1: // Excel
ExportToExcel(data, fileName);
break;
case 2: // CSV
ExportToCsv(data, fileName);
break;
case 3: // XML
ExportToXml(data, fileName);
break;
}
}
}
Configuration Save Dialog
public class ConfigurationManager
{
public void SaveConfiguration(AppSettings settings)
{
using var saveDialog = new KryptonSaveFileDialog
{
Title = "Save Configuration",
Filter = "Configuration Files (*.config)|*.config|JSON Files (*.json)|*.json|XML Files (*.xml)|*.xml",
FilterIndex = 1,
DefaultExt = "config",
InitialDirectory = GetConfigDirectory(),
FileName = "MyAppSettings",
OverwritePrompt = true,
CheckWriteAccess = true
};
// Add application-specific custom places
saveDialog.CustomPlaces.Add(GetConfigDirectory());
saveDialog.CustomPlaces.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyApp"));
if (saveDialog.ShowDialog() == DialogResult.OK)
{
SaveSettings(settings, saveDialog.FileName, saveDialog.FilterIndex);
}
}
private void SaveSettings(AppSettings settings, string fileName, int formatIndex)
{
switch (formatIndex)
{
case 1: // .config format
SaveAsConfig(settings, fileName);
break;
case 2: // JSON format
SaveAsJson(settings, fileName);
break;
case 3: // XML format
SaveAsXml(settings, fileName);
break;
}
}
}
Integration Patterns
MVVM Pattern Integration
public class SaveFileDialogService : ISaveFileDialogService
{
public SaveFileResult ShowSaveDialog(SaveFileDialogOptions options)
{
using var dialog = new KryptonSaveFileDialog
{
Title = options.Title,
Filter = options.Filter,
FilterIndex = options.FilterIndex,
DefaultExt = options.DefaultExtension,
InitialDirectory = options.InitialDirectory,
FileName = options.DefaultFileName,
OverwritePrompt = options.OverwritePrompt,
CreatePrompt = options.CreatePrompt
};
var result = new SaveFileResult();
if (dialog.ShowDialog() == DialogResult.OK)
{
result.Success = true;
result.FileName = dialog.FileName;
result.FilterIndex = dialog.FilterIndex;
}
return result;
}
}
public class SaveFileDialogOptions
{
public string Title { get; set; } = "Save File";
public string Filter { get; set; } = "All Files (*.*)|*.*";
public int FilterIndex { get; set; } = 1;
public string DefaultExtension { get; set; } = string.Empty;
public string InitialDirectory { get; set; } = string.Empty;
public string DefaultFileName { get; set; } = string.Empty;
public bool OverwritePrompt { get; set; } = true;
public bool CreatePrompt { get; set; } = false;
}
public class SaveFileResult
{
public bool Success { get; set; }
public string FileName { get; set; } = string.Empty;
public int FilterIndex { get; set; }
}
Async File Operations
public class AsyncFileSaver
{
public async Task<bool> SaveFileAsync(string content, string defaultName)
{
var saveDialog = new KryptonSaveFileDialog
{
Title = "Save File",
FileName = defaultName,
Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*",
FilterIndex = 1,
DefaultExt = "txt",
OverwritePrompt = true
};
if (saveDialog.ShowDialog() == DialogResult.OK)
{
try
{
await File.WriteAllTextAsync(saveDialog.FileName, content);
return true;
}
catch (Exception ex)
{
MessageBox.Show($"Error saving file: {ex.Message}", "Save Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
return false;
}
}
Design-Time Integration
Visual Studio Designer
- Toolbox Integration: Available with custom bitmap representation
- Property Window: All standard
SaveFileDialogproperties available - Theme Integration: Automatic Krypton theming applied
- Serialization: Design-time settings saved to designer files
Property Categories
- Behavior: Core dialog functionality (
CreatePrompt,OverwritePrompt,CheckWriteAccess) - Data: File and path properties (
FileName,Filter,InitialDirectory) - Appearance: Visual properties (
Title)
Performance Considerations
- Theme Integration: Lightweight wrapper with minimal overhead
- Memory Management: Proper disposal of internal dialog resources
- Thread Safety: Standard Windows Forms thread safety applies
- Resource Cleanup: Automatic cleanup through IDisposable implementation
Common Issues and Solutions
File Access Denied
Issue: Save operation fails with access denied
Solution: Use CheckWriteAccess property and handle exceptions:
saveDialog.CheckWriteAccess = true;
saveDialog.FileOk += (sender, e) =>
{
try
{
// Test write access
File.WriteAllText(saveDialog.FileName, "test");
File.Delete(saveDialog.FileName);
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("Access denied to the selected location.", "Access Denied",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
e.Cancel = true;
}
};
Filter Not Working
Issue: File filter not applied correctly
Solution: Ensure proper filter format:
// Correct format: "Description|Pattern|Description|Pattern"
saveDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
saveDialog.FilterIndex = 1; // Select first filter
Path Not Found
Issue: InitialDirectory doesn't exist
Solution: Validate directory before setting:
string initialDir = GetInitialDirectory();
if (Directory.Exists(initialDir))
{
saveDialog.InitialDirectory = initialDir;
}
else
{
saveDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
}
Migration from Standard SaveFileDialog
Direct Replacement
// Old code
SaveFileDialog saveDialog = new SaveFileDialog();
// New code
KryptonSaveFileDialog saveDialog = new KryptonSaveFileDialog();
Enhanced Features
// Standard SaveFileDialog (basic)
var standardSfd = new SaveFileDialog();
// KryptonSaveFileDialog (enhanced)
var kryptonSfd = new KryptonSaveFileDialog
{
Title = "Save Document",
Filter = "Text Files (*.txt)|*.txt",
OverwritePrompt = true,
CreatePrompt = false,
CheckWriteAccess = true, // .NET 8.0+ feature
CustomPlaces = // Enhanced custom places support
};
Real-World Integration Examples
Document Editor Save
public partial class DocumentEditor : Form
{
private string currentDocument = string.Empty;
private bool isModified = false;
private void SaveDocument()
{
if (string.IsNullOrEmpty(currentDocument))
{
SaveAsDocument();
}
else
{
SaveToCurrentFile();
}
}
private void SaveAsDocument()
{
using var saveDialog = new KryptonSaveFileDialog
{
Title = "Save Document As",
Filter = "Rich Text Format (*.rtf)|*.rtf|Text Files (*.txt)|*.txt|Word Documents (*.docx)|*.docx",
FilterIndex = 1,
DefaultExt = "rtf",
InitialDirectory = GetLastSaveDirectory(),
OverwritePrompt = true,
CheckWriteAccess = true
};
// Add recent folders as custom places
foreach (string recentFolder in GetRecentFolders())
{
if (Directory.Exists(recentFolder))
{
saveDialog.CustomPlaces.Add(recentFolder);
}
}
if (saveDialog.ShowDialog() == DialogResult.OK)
{
SaveToFile(saveDialog.FileName, saveDialog.FilterIndex);
currentDocument = saveDialog.FileName;
isModified = false;
UpdateTitle();
AddToRecentFiles(saveDialog.FileName);
}
}
private void SaveToFile(string fileName, int formatIndex)
{
try
{
switch (formatIndex)
{
case 1: // RTF
richTextBox.SaveFile(fileName, RichTextBoxStreamType.RichText);
break;
case 2: // TXT
File.WriteAllText(fileName, richTextBox.Text);
break;
case 3: // DOCX
SaveAsWordDocument(fileName);
break;
}
}
catch (Exception ex)
{
MessageBox.Show($"Error saving file: {ex.Message}", "Save Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
Image Export Dialog
public class ImageExporter
{
public void ExportImage(Image image)
{
using var saveDialog = new KryptonSaveFileDialog
{
Title = "Export Image",
Filter = "PNG Files (*.png)|*.png|JPEG Files (*.jpg)|*.jpg|Bitmap Files (*.bmp)|*.bmp|TIFF Files (*.tiff)|*.tiff",
FilterIndex = 1,
DefaultExt = "png",
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
OverwritePrompt = true,
FileName = "ExportedImage"
};
if (saveDialog.ShowDialog() == DialogResult.OK)
{
ExportImageToFormat(image, saveDialog.FileName, saveDialog.FilterIndex);
}
}
private void ExportImageToFormat(Image image, string fileName, int formatIndex)
{
try
{
switch (formatIndex)
{
case 1: // PNG
image.Save(fileName, ImageFormat.Png);
break;
case 2: // JPEG
image.Save(fileName, ImageFormat.Jpeg);
break;
case 3: // BMP
image.Save(fileName, ImageFormat.Bmp);
break;
case 4: // TIFF
image.Save(fileName, ImageFormat.Tiff);
break;
}
}
catch (Exception ex)
{
MessageBox.Show($"Error exporting image: {ex.Message}", "Export Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}